본문으로 건너뛰기

· 약 6분
Dongmin Yu

개발자로 한국에서 취직을 하려면 로드맵을 어떻게 정하는게 좋을지 궁금하신 것 같습니다. 웹 개발자는 크게 프론트엔드와 백엔드로 나뉘는데요, 프론트엔드는 웹사이트나 웹앱의 사용자 인터페이스를 담당하는 파트이고, 백엔드는 서버와 데이터베이스를 다루는 파트입니다. 프론트엔드 개발자가 되고 싶으시다면 JavaScript부터 공부하셔야 합니다. JavaScript는 웹사이트나 웹앱에 동적인 기능과 상호작용을 추가할 수 있는 언어입니다. 프론트엔드 개발자 로드맵에 따르면, HTML/CSS와 JavaScript의 기본기를 다지신 후에는 다음과 같은 주제들을 공부하시면 좋습니다.

  • 웹 브라우저의 동작 방식과 성능 최적화
  • 반응형 디자인과 CSS 프레임워크
  • 자바스크립트 라이브러리와 프레임워크 (예: React, Vue, Angular)
  • API와 HTTP 통신
  • 버전 관리 시스템 (예: Git)
  • 테스트 및 디버깅 도구
  • 타입스크립트와 GraphQL과 같은 심화 주제

만약 백엔드 개발자가 되고 싶으시다면, HTML/CSS와 JavaScript 외에도 서버 사이드 언어 (예: Python, Java, Node.js)와 데이터베이스 (예: MySQL, MongoDB)를 배우셔야 합니다. 백엔드 개발자 로드맵에 따르면, 서버 사이드 언어와 데이터베이스의 기본기를 다지신 후에는 다음과 같은 주제들을 공부하시면 좋습니다.

  • RESTful API 설계와 구현
  • 인증과 보안
  • 캐싱과 로깅
  • 클라우드 서비스 (예: AWS, Azure)
  • 배포 및 운영 방법
  • 마이크로서비스와 컨테이너화

위의 주제들은 모두 중요하고 유용하지만, 모든 것을 한 번에 배울 수 없습니다. 그러므로 자신의 목표와 관심 분야에 맞춰서 우선순위를 정하고 차근차근 공부해 나가시기 바랍니다. 비전공자로서 단기간에 개발자로 취직을 하려면 많은 시간과 노력이 필요할 것입니다. 하지만 포기하지 마시고 꾸준히 학습하시면 분명 성장할 수 있습니다. 저도 여러분의 성공을 응원합니다!

덧붙여서 직접 만든 작품들을 포트폴리오로 정리해 두시면 취직에 도움이 됩니다. 포트폴리오를 만들 때 참고할 수 있는 것은 여러 가지가 있습니다. 예를 들어,

  • 무료 템플릿을 이용해서 깔끔하고 전문적인 포트폴리오를 만들 수 있는 웹사이트
  • 개발자의 포트폴리오와 이력서 작성법에 대한 블로그 글
  • 스타트업 취업이 되는 개발자 포트폴리오 만드는 법에 대한 커뮤니티 게시글

등이 있습니다. 포트폴리오를 만들 때는 자신의 작업물을 잘 보여줄 수 있는 디자인과 내용을 선택하고, 자신이 한 부분과 역할을 명확하게 설명해주시면 좋습니다. 또한 포트폴리오의 주제와 분야가 자신이 지원하는 회사나 직무와 관련이 있도록 하시면 더욱 효과적입니다.

  1. [포트폴리오・이력서] 개발자의 포트폴리오・이력서 작성법 ...
  2. OKKY - 스타트업 취업이 무조건 되는 개발자 포트폴리오 만들기
  3. 포트폴리오 만들기. 무료 템플릿으로 만드는 깔끔한 포트폴리오 ...
  4. 2023 프론트엔드 개발자 로드맵 ㅣ공부순서 총정리 (한글 ver ...
  5. 로드맵 (공부순서) - 드림코딩 아카데미 | Dream Coding Academy
  6. 2023 백엔드 개발자 로드맵 feat. 현직 백엔드 개발자 | zero-base

· 약 4분
Dongmin Yu

자바 파일은 크게 필드 + 생성자+ 메서드로 구성되어 있고 그중 필드 부분에서 선언한 전역변수 와 정적 멤버변수 이 두가지는 static 영역에 데이터를 저장하고 사용한다. 이 영역에서 데이터는 프로그램의 시작부터 종료될 때까지 메모리에 남아 있다.

전역변수를 무분별하게 사용하면 메모리가 부족할 우려가 있으니 필요한 변수만 사용하기!

  • 패키지나 클래스 정보가 호출되는 시점에 즉시 메소드 영역에 올라간다
  • static 이 붙은 클래스 멤버 필드, 멤버메서드 역시 포함이다
  • JVM 종료 시점까지 사라지지 않고 유지된다
  • 클래스, 메소드 영역이라고 불린다.
  1. 클래스 변수
    • static 키워드를 가지고 필드에 선언하는 변수 = 정적 필드 (메모리의 static 영역 사용)
    • 프로그램 시작시 생성, 프로그램 종료시 소멸
  2. 멤버 변수(인스턴스 변수)
    • static 키워드 없이 필드에 선언하는 변수 (메모리의 heap영역 사용)
    • 인스턴스 생성 시 생성, 참조하지 않을시 소멸(가비지 컬렉터가 소멸시킴)
  3. 지역 변수
    • 메소드, 생성자, 초기화 블록 내부에서 선언하는 변수
    • 메소드 호출 시 생성, 메소드 종료시 소멸
    • JVM은 자바 프로그램을 실행하기 위한 가상 머신, 자바 바이트코드를 실행하고 메모리 관리와 가비지 컬렉션(GC) 등의 작업을 수행.
    • 프로세스는 운영체제에서 실행 중인 프로그램을 의미. 각 프로세스는 독립된 메모리 공간을 할당받아 사용, 다른 프로세스와 데이터를 공유하려면 특별한 방법(IPC)이 필요.
    • 스레드는 프로세스 내에서 실행되는 작업의 단위. 하나의 프로세스에 여러 개의 스레드가 존재할 수 있으며 이들은 같은 메모리 공간을 공유하면서 동시에 작업을 수행 가능.
    • JVM과 프로세스와 스레드는 서로 다른 개념이지만 관계 있음. 자바 프로그램이 실행되면 JVM이 시작되고 이 JVM은 하나의 프로세스. 이 JVM 프로세스 내부에서 여러 개의 스레드가 생성되어 작업을 수행.

· 약 3분
Dongmin Yu

Summary

RDBMS에서 가장 많이 사용되는 데이터베이스 각각의 특징과 장단점

  1. Oracle: Oracle Corporation에서 개발되었으며 현재 가장 널리 사용되는 RDBMS입니다. 사용자 정의 유형, 상속 및 다형성과 같은 객체 지향 기능을 구현하는 RDBMS를 객체 관계형 데이터베이스 관리 시스템 (ORDBMS)이라고 합니다. Oracle Database는 관계형 모델을 객체 관계형 모델로 확장하여 복잡한 비즈니스 모델을 관계형 데이터베이스에 저장할 수 있습니다.

  2. MySQL: MySQL은 오픈 소스이며 무료이고 세계에서 가장 인기있는 DB입니다.

    MySQL은 관계형 데이터베이스입니다. MySQL소프트웨어는 오픈소스입니다. MySQL데이터베이스 서버는 매우 빠르고 안정적이며 확장 가능하며 사용하기 쉽습니다. MySQL서버는 클라이언트 / 서버 또는 임베디드 시스템에서 작동합니다.

  3. SQL Server: Microsoft에서 개발한 SQL Server는 세계에서 가장 널리 사용되는 데이터베이스 중 하나입니다. C, C++로 작성되어 있고 SQL Server는 Microsoft Azure Cloud의 일부입니다.

  4. PostgreSQL: PostgreSQL은 복잡한 데이터 워크로드를 안전하게 저장하며 SQL 언어를 사용하고 확장하는 오픈 소스 객체 관계형 데이터베이스 시스템입니다. PostgreSQL은 아키텍처, 안정성과 데이터 무결성, 강력한 기능 세트, 확장성 및 소프트웨어 뒤의 오픈소스 커뮤니티의 헌신으로 성능과 혁신적인 솔루션을 지속적으로 제공하는 것으로 유명합니다.

References

(1) [데이터베이스] RDBMS 종류와 특성 - 차근차근 (2) 관계형과 비관계형 데이터 베이스의 종류와 특징

· 약 2분
Dongmin Yu

Question

⚙ <fe0f> gear Unicode: `U+2699` `U+FE0F`, `UTF-8: E2 9A 99 EF B8 8F`

Write a regular expression that matches all Unicode emojis of this type.

Answer

Sure, I can help you with that! Here's a regular expression that matches all Unicode emojis of this type:

\u2699[\uFE0E\uFE0F]

This regular expression matches the gear Unicode character (U+2699) followed by either the text presentation selector (U+FE0E) or the emoji presentation selector (U+FE0F). The "g" at the end of the regular expression means that it will match all occurrences of this pattern in a given string.

I hope this helps! Let me know if you have any other questions.

Question

Write a regular expression that matches all Unicode emojis of this type.

Answer

Sure, I can help you with that! Here's a regular expression that matches all Unicode emojis of this type:

[\u{1f600}-\u{1f64f}]

This regular expression uses the Unicode code point range for emoticons, which is U+1F600 to U+1F64F. The /u flag at the end of the expression enables Unicode mode in JavaScript, which allows the matching of Unicode characters beyond the BMP (Basic Multilingual Plane).

This regular expression will match all emojis of this type, including smileys, people, animals, and other objects.

Question

Write a regular expression that matches all Unicode emoji characters of this type. not only people emojis but all of them

Answer

Sure, I can help you with that! Here's a regular expression that matches all Unicode emoji characters:

[\u{1F000}-\u{1F6FF}\u{1F900}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}]

This regular expression uses Unicode character ranges to match all the different types of emoji characters. The ranges include:

  • \u{1F000}-\u{1F6FF}: 기타 기호 및 그림, 이모티콘, 교통 및 지도 기호 등.
  • \u{1F900}-\u{1F9FF}: 보조 기호 및 픽토그램.
  • \u{2600}-\u{26FF}: 기타 기호, 딩벳 등.
  • \u{2700}-\u{27BF}: 딩벳, 보조 화살표-C 등.
  • \u{1F300}-\u{1F5FF}: 날씨, 시간 등을 포함한 기타 기호 및 픽토그램.
  • \u{1F680}-\u{1F6FF}: 자동차, 자전거, 비행기 등을 포함한 교통수단 및 지도 기호.
  • \u{1F1E0}-\u{1F1FF}: 국가 국기를 나타내는 데 사용되는 지역 표시 기호.

I hope this helps! Let me know if you have any other questions.

· 약 6분
Dongmin Yu

일반적으로 스마트폰을 이용해 웹사이트에 접속하면 모바일용 사이트로 리다이렉팅하는 경우가 많습니다. 이러한 리다이렉팅은 서버 측 리다이렉트 방식을 사용하여 구현할 수 있습니다. 서버 측 리다이렉트 방식은 HTTP 상태 코드 301 또는 302를 사용하여 클라이언트에게 새로운 URL로 이동하도록 지시합니다. 이 방식은 빠르게 작동하며, 검색엔진 최적화(SEO)에도 유리합니다. 하지만, 깃허브 페이지에서는 서버 측 리다이렉트를 직접 설정할 수 없습니다. 하지만, 깃허브 페이지의 정적 사이트에서 메타 리프레시 태그를 사용하여 리다이렉트를 구현할 수 있습니다. 메타 리프레시 태그는 <meta http-equiv="refresh" content="0;url=https://new-website.com">와 같은 형태로 사용할 수 있습니다. 이 태그는 content 속성의 첫 번째 값으로 지정된 시간(초) 후에 url로 지정된 페이지로 이동합니다. 이 방식은 자바스크립트 리다이렉트 방식보다 빠르게 작동하며, 검색엔진 최적화(SEO)에도 더 유리합니다. 메타 리프레시 태그는 웹 페이지를 다른 URL로 자동으로 리다이렉트하는 데 사용됩니다. 이 방식의 장점은 다음과 같습니다.

  1. 쉽게 구현할 수 있습니다. 메타 리프레시 태그는 HTML 코드에 추가하기만 하면 됩니다.
  2. 자바스크립트가 비활성화되어 있어도 작동합니다. 메타 리프레시 태그는 HTML 코드에 포함되므로, 자바스크립트가 비활성화되어 있어도 작동합니다. 하지만, 메타 리프레시 태그에도 몇 가지 단점이 있습니다.
  3. 검색엔진 최적화(SEO)에 불리할 수 있습니다. 메타 리프레시 태그는 검색엔진이 인식하기 어려울 수 있으며, 검색엔진 최적화(SEO)에 불리할 수 있습니다.
  4. 사용자 경험을 저해할 수 있습니다. 메타 리프레시 태그로 인해 페이지가 자동으로 이동하면, 사용자가 원하지 않는 페이지로 이동될 수 있습니다. 이로 인해 사용자 경험이 저해될 수 있습니다. 메타 리프레시 태그를 사용할 때는 이러한 장단점을 고려하여 적절하게 사용하는 것이 좋습니다.

깃허브 페이지의 정적 사이트에서 자바스크립트 리다이렉트 메소드로 유저를 이동시키는 경우, 1초 정도의 지연이 발생할 수 있습니다.

한 페이지에서 메타 태그 리다이렉트와 자바스크립트 리다이렉트를 병행하여 사용하는 경우, 다음과 같은 문제가 발생할 수 있습니다.

  1. 사용자가 원하지 않는 페이지로 이동될 수 있습니다. 메타 태그 리다이렉트와 자바스크립트 리다이렉트가 서로 다른 URL로 이동하도록 설정된 경우, 사용자가 원하지 않는 페이지로 이동될 수 있습니다.
  2. 검색엔진 최적화(SEO)에 불리할 수 있습니다. 검색엔진은 한 페이지에서 여러 가지 리다이렉팅 방식이 사용되는 경우, 해당 페이지의 콘텐츠를 정확하게 인식하기 어려울 수 있습니다. 이로 인해 검색엔진 최적화(SEO)에 불리할 수 있습니다. 따라서, 한 페이지에서는 하나의 리다이렉팅 방식만 사용하는 것이 좋습니다. 리다이렉팅 방식을 선택할 때는 상황에 따라 적절한 방식을 선택하는 것이 중요합니다.

· 약 14분
Dongmin Yu

다음은 NativeBatteryProvider라는 이름의 컴포넌트를 만드는 예시 코드입니다. 이 컴포넌트는 기기의 배터리 잔량을 정기적으로 쿼리하여 State<number> 형태로 하위 컴포넌트들에게 전달합니다.

import React, { createContext, useState, useEffect } from "react";
import { NativeModules } from "react-native";
const BatteryContext = createContext();
const NativeBatteryProvider = ({ children }) => {
  const [batteryLevel, setBatteryLevel] = useState(null);
  useEffect(() => {
    const interval = setInterval(() => {
      NativeModules.BatteryStatus.getBatteryStatus((level) => {
        setBatteryLevel(level);
      });
    }, 1000);
    return () => clearInterval(interval);
  }, []);
  return (
    <BatteryContext.Provider value={batteryLevel}>
            {children}
    </BatteryContext.Provider>
  );
};
export { BatteryContext, NativeBatteryProvider };

이 예제에서는 NativeModules.BatteryStatus.getBatteryStatus 메소드를 사용하여 배터리 잔량을 가져옵니다. 이 메소드는 네이티브 모듈에서 구현해야 합니다. 또한 setInterval 함수를 사용하여 1초마다 배터리 잔량을 쿼리합니다. 이제 다른 컴포넌트에서 useContext Hook을 사용하여 BatteryContext의 값을 가져올 수 있습니다.

import React, { useContext } from "react";
import { Text } from "react-native";
import { BatteryContext } from "./NativeBatteryProvider";
const BatteryLevel = () => {
  const batteryLevel = useContext(BatteryContext);
  return <Text>Battery Level: {batteryLevel}</Text>;
};

이 예제에서는 useContext Hook을 사용하여 BatteryContext의 값을 가져와서 화면에 표시합니다.

특정 무선 이어폰을 위한 네이티브 블루투스 모듈을 사용하여 여러가지 동작을 실행하고 이벤트 응답을 받으려면 다음과 같은 단계를 따르십시오:

  1. 네이티브 모듈에서 블루투스 이벤트를 구독하고 이벤트가 발생할 때마다 React Native로 전달합니다. 이를 위해 NativeEventEmitter 클래스를 사용할 수 있습니다.
  2. React Native에서 NativeEventEmitter 인스턴스를 생성하고 addListener 메소드를 사용하여 이벤트를 구독합니다.
  3. 이벤트가 발생할 때마다 상태를 업데이트하고 하위 컴포넌트들에게 전달합니다. 이를 위해 Context.ProvideruseContext Hook을 사용할 수 있습니다. 다음은 이러한 기능을 구현하는 예시 코드입니다.
import React, { createContext, useState, useEffect } from "react";
import { NativeModules, NativeEventEmitter } from "react-native";
const BluetoothContext = createContext();
const BluetoothProvider = ({ children }) => {
  const [batteryLevel, setBatteryLevel] = useState(null);
  useEffect(() => {
    const eventEmitter = new NativeEventEmitter(NativeModules.BluetoothModule);
    const subscription = eventEmitter.addListener(
      "BatteryLevelChanged",

      (level) => {
        setBatteryLevel(level);
      },
    );
    return () => subscription.remove();
  }, []);
  return (
    <BluetoothContext.Provider value={{ batteryLevel }}>
            {children}
    </BluetoothContext.Provider>
  );
};
export { BluetoothContext, BluetoothProvider };

이 예제에서는 NativeEventEmitter 클래스를 사용하여 네이티브 모듈에서 발생하는 BatteryLevelChanged 이벤트를 구독합니다. 이벤트가 발생할 때마다 상태를 업데이트하고 하위 컴포넌트들에게 전달합니다. 이제 다른 컴포넌트에서 useContext Hook을 사용하여 BluetoothContext의 값을 가져올 수 있습니다.

import React, { useContext } from "react";
import { Text } from "react-native";
import { BluetoothContext } from "./BluetoothProvider";
const BatteryLevel = () => {
  const { batteryLevel } = useContext(BluetoothContext);
  return <Text>Battery Level: {batteryLevel}</Text>;
};

이 예제에서는 useContext Hook을 사용하여 BluetoothContext의 값을 가져와서 화면에 표시합니다. 음악 재생 출력 변경 등의 메소드를 실행하려면 네이티브 모듈에서 해당 기능을 구현하고 React Native에서 해당 메소드를 호출해야 합니다.

커스텀 훅이나 컨텍스트 프로바이더를 만드는 것에 대한 아키텍쳐 가이드는 다양한 방법이 있습니다. 일반적으로는 앱의 상태를 전역적으로 관리하고 하위 컴포넌트들에게 전달하는 데 사용됩니다. 다음은 NativeModules.BluetoothModule.changePlayback 메소드를 호출하고 changePlaybackResult 이벤트를 리턴받아 상태를 업데이트하는 예시 코드입니다.

import React, { createContext, useState, useEffect } from "react";
import { NativeModules, NativeEventEmitter } from "react-native";
const BluetoothContext = createContext();
const BluetoothProvider = ({ children }) => {
  const [playbackDevice, setPlaybackDevice] = useState(null);
  useEffect(() => {
    const eventEmitter = new NativeEventEmitter(NativeModules.BluetoothModule);
    const subscription = eventEmitter.addListener(
      "changePlaybackResult",

      (device) => {
        setPlaybackDevice(device);
      },
    );
    return () => subscription.remove();
  }, []);
  const changePlayback = () => {
    NativeModules.BluetoothModule.changePlayback();
  };
  return (
    <BluetoothContext.Provider value={{ playbackDevice, changePlayback }}>
            {children}
    </BluetoothContext.Provider>
  );
};
export { BluetoothContext, BluetoothProvider };

이 예제에서는 NativeEventEmitter 클래스를 사용하여 네이티브 모듈에서 발생하는 changePlaybackResult 이벤트를 구독합니다. 이벤트가 발생할 때마다 상태를 업데이트하고 하위 컴포넌트들에게 전달합니다. 또한 changePlayback 함수를 정의하여 NativeModules.BluetoothModule.changePlayback 메소드를 호출합니다. 이제 다른 컴포넌트에서 useContext Hook을 사용하여 BluetoothContext의 값을 가져올 수 있습니다.

import React, { useContext } from "react";
import { Text, Button } from "react-native";
import { BluetoothContext } from "./BluetoothProvider";
const PlaybackDevice = () => {
  const { playbackDevice, changePlayback } = useContext(BluetoothContext);
  return (
    <>
            <Text>Playback Device: {playbackDevice}</Text>
            <Button onPress={changePlayback} title="Change Playback" />
    </>
  );
};

이 예제에서는 useContext Hook을 사용하여 BluetoothContext의 값을 가져와서 화면에 표시합니다. 또한 Button 컴포넌트의 onPress prop에 changePlayback 함수를 전달하여 버튼을 클릭하면 NativeModules.BluetoothModule.changePlayback 메소드가 호출되도록 합니다.

Context.Providervalue prop에 전달하는 객체에는 프로바이더에서 관리하는 모든 상태와 메소드를 포함하는 것이 일반적입니다. 이렇게 하면 하위 컴포넌트들이 useContext Hook을 사용하여 상태와 메소드를 가져올 수 있습니다. 예를 들어, 다음과 같은 코드에서는 BluetoothContext.Providervalue prop에 전달하는 객체에 playbackDevice 상태와 changePlayback 메소드가 포함되어 있습니다.

const BluetoothProvider = ({ children }) => {
  const [playbackDevice, setPlaybackDevice] = useState(null);
  const changePlayback = () => {
    console.log("changed...!");
  };
  return (
    <BluetoothContext.Provider value={{ playbackDevice, changePlayback }}>
            {children}
    </BluetoothContext.Provider>
  );
};

이제 다른 컴포넌트에서 useContext Hook을 사용하여 BluetoothContext의 값을 가져올 수 있습니다.

const PlaybackDevice = () => {
  const { playbackDevice, changePlayback } = useContext(BluetoothContext);
};

이 예제에서는 useContext Hook을 사용하여 BluetoothContext의 값을 가져와서 playbackDevice 상태와 changePlayback 메소드를 사용할 수 있습니다.

useContext Hook과 Context.Provider를 사용하지 않는 다른 접근 방법도 있습니다. 예를 들어, Redux와 같은 상태관리 라이브러리를 사용하여 앱의 상태를 전역적으로 관리할 수 있습니다. Redux는 전역 상태 저장소를 제공하고 액션을 사용하여 상태를 업데이트합니다. 이를 통해 앱의 모든 컴포넌트에서 상태를 가져오고 업데이트할 수 있습니다. 다음은 Redux를 사용하여 앱의 상태를 관리하는 예시 코드입니다.

import { createStore } from "redux";
// 리듀서 함수 정의

const reducer = (state = { playbackDevice: null }, action) => {
  switch (action.type) {
    case "SET_PLAYBACK_DEVICE":
      return { ...state, playbackDevice: action.device };
    default:
      return state;
  }
};
// 스토어 생성

const store = createStore(reducer);
// 스토어 구독

store.subscribe(() => {
  console.log(store.getState());
});
// 액션 디스패치

store.dispatch({ type: "SET_PLAYBACK_DEVICE", device: "Speaker" });

이 예제에서는 createStore 함수를 사용하여 Redux 스토어를 생성합니다. 또한 reducer 함수를 정의하여 상태를 업데이트하는 방법을 지정합니다. 이후 store.subscribe 메소드를 사용하여 스토어의 변경 사항을 구독하고 store.dispatch 메소드를 사용하여 액션을 디스패치합니다. 이 방법 외에도 MobX와 같은 다른 상태관리 라이브러리를 사용할 수도 있습니다. 이러한 라이브러리는 각각 고유한 방식으로 상태관리를 제공합니다.

useContext Hook과 Context.Provider를 사용하는 방법은 외부 라이브러리를 사용하지 않고 전역 상태를 관리하는 일반적인 방법 중 하나입니다. 그러나 이것이 유일한 방법은 아닙니다. 예를 들어, React의 props를 사용하여 상위 컴포넌트에서 하위 컴포넌트로 상태와 메소드를 전달할 수 있습니다. 이 방법은 컴포넌트 계층이 깊지 않은 경우에 적합합니다. 다음은 props를 사용하여 상위 컴포넌트에서 하위 컴포넌트로 상태와 메소드를 전달하는 예시 코드입니다.

import React, { useState } from "react";
import { Text, Button } from "react-native";
const Parent = () => {
  const [playbackDevice, setPlaybackDevice] = useState(null);
  const changePlayback = () => {
    console.log("changed...!");
  };
  return (
    <Child playbackDevice={playbackDevice} changePlayback={changePlayback} />
  );
};
const Child = ({ playbackDevice, changePlayback }) => {
  return (
    <>
            <Text>Playback Device: {playbackDevice}</Text>
            <Button onPress={changePlayback} title="Change Playback" />
    </>
  );
};

이 예제에서는 Parent 컴포넌트에서 useState Hook을 사용하여 상태를 정의하고 Child 컴포넌트의 props로 전달합니다. 또한 changePlayback 함수를 정의하고 Child 컴포넌트의 props로 전달합니다. 이 방법은 간단한 경우에 적합하지만 컴포넌트 계층이 깊어지면 복잡해질 수 있습니다. 이 경우에는 useContext Hook과 Context.Provider를 사용하는 것이 더 적합할 수 있습니다.

RefObjectuseRef Hook은 컴포넌트의 인스턴스 또는 DOM 요소에 대한 참조를 저장하는 데 사용됩니다. 이러한 기능은 상태관리와는 직접적으로 관련이 없습니다. 그러나 useRef Hook을 사용하여 컴포넌트 간에 데이터를 전달하는 데 사용할 수 있습니다. 예를 들어, forwardRef 함수를 사용하여 하위 컴포넌트의 메소드를 상위 컴포넌트에서 호출할 수 있습니다. 다음은 useRef Hook과 forwardRef 함수를 사용하여 하위 컴포넌트의 메소드를 상위 컴포넌트에서 호출하는 예시 코드입니다.

import React, { useRef, forwardRef } from "react";
import { Button } from "react-native";
const Parent = () => {
  const childRef = useRef();
  const handleClick = () => {
    childRef.current.changePlayback();
  };
  return (
    <>
            <Child ref={childRef} />
            <Button onPress={handleClick} title="Change Playback" />
    </>
  );
};
const Child = forwardRef((props, ref) => {
  const changePlayback = () => {
    console.log("changed...!");
  }; // ref 객체에 메소드 할당
  ref.current = { changePlayback };
  return null;
});

이 예제에서는 Parent 컴포넌트에서 useRef Hook을 사용하여 childRef 객체를 생성합니다. 이 객체는 Child 컴포넌트의 인스턴스에 대한 참조를 저장합니다. 또한 Button 컴포넌트의 onPress prop에 handleClick 함수를 전달하여 버튼을 클릭하면 Child 컴포넌트의 changePlayback 메소드가 호출되도록 합니다. Child 컴포넌트에서는 forwardRef 함수를 사용하여 ref prop을 전달받습니다. 이 prop은 상위 컴포넌트에서 생성한 childRef 객체와 동일합니다. 이제 이 객체의 current 속성에 changePlayback 메소드를 할당하여 상위 컴포넌트에서 호출할 수 있도록 합니다. 이 방법은 특정 경우에 유용할 수 있지만 일반적으로는 상태관리에 사용되지 않습니다. 상태관리에는 일반적으로 useContext Hook과 Context.Provider, 또는 외부 상태관리 라이브러리가 사용됩니다.

· 약 22분
Dongmin Yu

What is MONAD and How Can we Implement it

프로그래밍에서 모나드는 함수형 프로그래밍에서 사용되는 디자인 패턴 중 하나입니다. 모나드는 값을 캡슐화하고, 그 값을 변환하는 함수를 체이닝하는 방식으로 작동합니다. 이를 통해 코드의 가독성을 높이고, 부수 효과를 제어할 수 있습니다. Haskell 언어에서 모나드는 매우 중요한 개념으로 사용됩니다. 스칼라에서 모나드는 트레이트(trait)로 구현됩니다. 모나드는 flatMapunit이라는 두 가지 메소드를 가지고 있습니다. 다음은 스칼라에서 Option 모나드의 예제입니다.

val opt1: Option[Int] = Some(3)
val opt2: Option[Int] = Some(5)
val opt3: Option[Int] = None
val result = for {
  x <- opt1
  y <- opt2
} yield x + y
println(result) // Some(8)
val result2 = for {
  x <- opt1
  y <- opt3
} yield x + y
println(result2) // None

위의 예제에서 for 구문은 flatMapmap을 사용하여 변환됩니다. Option 모나드는 값이 있을 수도 있고 없을 수도 있는 상황에서 안전한 연산을 가능하게 합니다. flatMapunit은 모나드의 두 가지 기본 연산입니다. flatMap은 모나드 값을 받아서 다른 모나드 값을 반환하는 함수를 적용하는 연산입니다. 이를 통해 모나드 값을 변환하고 체이닝할 수 있습니다. unit은 일반 값을 모나드 값으로 변환하는 연산입니다. 다음은 스칼라에서 List 모나드의 flatMapunit 예제입니다.

val list1 = List(1, 2, 3)
val list2 = List(4, 5, 6)
val result = list1.flatMap(x => list2.map(y => x + y))
println(result) // List(5, 6, 7, 6, 7, 8, 7, 8, 9)
val unitResult = List.unit(3)
println(unitResult) // List(3)

위의 예제에서 flatMaplist1의 각 요소에 대해 함수를 적용하여 새로운 리스트를 생성하고 이를 하나의 리스트로 합칩니다. unit은 일반 값을 리스트로 변환합니다. 자바스크립트는 함수형 프로그래밍을 지원하는 언어이기 때문에 모나드 패턴을 사용할 수 있습니다. 다음은 자바스크립트에서 Maybe 모나드의 예제입니다.

class Maybe {
  constructor(value) {
    this.value = value;
  }
  static of(value) {
    return new Maybe(value);
  }
  isNothing() {
    return this.value === null || this.value === undefined;
  }
  map(fn) {
    return this.isNothing() ? Maybe.of(null) : Maybe.of(fn(this.value));
  }
  flatMap(fn) {
    return this.map(fn).join();
  }
  join() {
    return this.isNothing() ? Maybe.of(null) : this.value;
  }
}
const maybe1 = Maybe.of(3);
const maybe2 = Maybe.of(null);
const result = maybe1.flatMap((x) => Maybe.of(x + 2));
console.log(result); // Maybe { value: 5 }
const result2 = maybe2.flatMap((x) => Maybe.of(x + 2));
console.log(result2); // Maybe { value: null }

위의 예제에서 Maybe 모나드는 값이 있을 수도 있고 없을 수도 있는 상황에서 안전한 연산을 가능하게 합니다. flatMapMaybe 값을 받아서 다른 Maybe 값을 반환하는 함수를 적용하는 연산입니다. 다음은 자바스크립트에서 Promise 모나드의 예제입니다.

const promise1 = Promise.resolve(3);
const promise2 = Promise.reject(new Error("An error occurred"));
const result = promise1.then((x) => x + 2);
result.then(console.log); // 5
const result2 = promise2.then((x) => x + 2);
result2.catch(console.log); // Error: An error occurred

위의 예제에서 Promise는 비동기 연산의 결과를 캡슐화하는 모나드입니다. then 메소드는 Promise 값을 받아서 다른 Promise 값을 반환하는 함수를 적용하는 연산입니다. 이를 통해 비동기 연산을 체이닝하고 에러 처리를 할 수 있습니다.

모나드 패턴의 장점

  1. 코드의 가독성 향상: 모나드를 사용하면 코드의 가독성이 높아집니다. 모나드는 값을 캡슐화하고, 그 값을 변환하는 함수를 체이닝하는 방식으로 작동하기 때문에 코드가 간결해집니다.
  2. 부수 효과 제어: 모나드를 사용하면 부수 효과를 제어할 수 있습니다. 예를 들어 Maybe 모나드는 값이 있을 수도 있고 없을 수도 있는 상황에서 안전한 연산을 가능하게 합니다. Promise 모나드는 비동기 연산의 결과를 캡슐화하여 에러 처리를 할 수 있게 합니다.
  3. 추상화: 모나드는 추상화 수준을 높여줍니다. 예를 들어 List 모나드는 리스트에 대한 연산을 추상화하여 코드의 재사용성을 높여줍니다.
  4. 함수형 프로그래밍 지원: 모나드는 함수형 프로그래밍 패러다임을 지원합니다. 함수형 프로그래밍은 순수 함수와 불변성을 강조하여 코드의 예측 가능성과 안정성을 높여줍니다.

타입스크립트에서 모나드를 구현할 때 주로 사용되는 키워드와 패턴

  1. 인터페이스와 제네릭: 타입스크립트에서 모나드는 인터페이스와 제네릭을 사용하여 구현됩니다. 인터페이스를 사용하면 모나드가 가져야 할 메소드를 정의할 수 있습니다. 제네릭을 사용하면 모나드가 다양한 타입의 값을 캡슐화할 수 있습니다.

    interface Monad<T> {
      flatMap<U>(fn: (value: T) => Monad<U>): Monad<U>;
      unit(value: T): Monad<T>;
    }
    
  2. 클래스와 메소드: 타입스크립트에서 모나드는 클래스로 구현됩니다. 클래스를 사용하면 모나드의 인스턴스를 생성하고 메소드를 정의할 수 있습니다.

    class Maybe<T> implements Monad<T> {
      constructor(private value: T | null) {}
      static of<T>(value: T | null) {
        return new Maybe(value);
      }
      flatMap<U>(fn: (value: T) => Maybe<U>): Maybe<U> {
        return this.value === null ? Maybe.of(null) : fn(this.value);
      }
      unit(value: T): Maybe<T> {
        return Maybe.of(value);
      }
    }
    
  3. 함수 체이닝: 타입스크립트에서 모나드는 함수 체이닝 패턴을 사용하여 작동합니다. flatMap 메소드를 사용하면 모나드 값을 변환하고 체이닝할 수 있습니다.

    const maybe1 = Maybe.of(3);
    const maybe2 = Maybe.of(null);
    const result = maybe1.flatMap((x) => Maybe.of(x + 2));
    console.log(result); // Maybe { value: 5 }
    const result2 = maybe2.flatMap((x) => Maybe.of(x + 2));
    console.log(result2); // Maybe { value: null }
    

다음은 타입스크립트에서 Either 모나드의 예제입니다.

type Either<L, R> = Left<L, R> | Right<L, R>;
class Left<L, R> {
  constructor(readonly value: L) {}
  static of<L, R>(value: L) {
    return new Left<L, R>(value);
  }
  flatMap<U>(fn: (value: R) => Either<L, U>): Either<L, U> {
    return this as Either<L, U>;
  }
  unit(value: R): Either<L, R> {
    return Right.of(value);
  }
}
class Right<L, R> {
  constructor(readonly value: R) {}
  static of<L, R>(value: R) {
    return new Right<L, R>(value);
  }
  flatMap<U>(fn: (value: R) => Either<L, U>): Either<L, U> {
    return fn(this.value);
  }
  unit(value: R): Either<L, R> {
    return Right.of(value);
  }
}
const either1: Either<string, number> = Right.of(3);
const either2: Either<string, number> = Left.of("An error occurred");
const result = either1.flatMap((x) => Right.of(x + 2));
console.log(result); // Right { value: 5 }
const result2 = either2.flatMap((x) => Right.of(x + 2));
console.log(result2); // Left { value: 'An error occurred' }

위의 예제에서 Either 모나드는 값을 캡슐화하고 에러 처리를 가능하게 합니다. flatMap 메소드는 Either 값을 받아서 다른 Either 값을 반환하는 함수를 적용하는 연산입니다. 이를 통해 연산을 체이닝하고 에러 처리를 할 수 있습니다. infer 키워드는 타입스크립트에서 조건부 타입을 정의할 때 사용되는 키워드입니다. infer 키워드를 사용하면 타입을 추론할 수 있습니다. infer 키워드는 모나드와 직접적인 관련이 없지만, 모나드를 구현할 때 유용하게 사용될 수 있습니다. 다음은 infer 키워드를 사용하여 Unwrap 타입을 정의하는 예제입니다.

type Unwrap<T> = T extends Monad<infer U> ? U : never;
interface Monad<T> {
  flatMap<U>(fn: (value: T) => Monad<U>): Monad<U>;
  unit(value: T): Monad<T>;
}
class Maybe<T> implements Monad<T> {
  constructor(private value: T | null) {}
  static of<T>(value: T | null) {
    return new Maybe(value);
  }
  flatMap<U>(fn: (value: T) => Maybe<U>): Maybe<U> {
    return this.value === null ? Maybe.of(null) : fn(this.value);
  }
  unit(value: T): Maybe<T> {
    return Maybe.of(value);
  }
}
const maybe1 = Maybe.of(3);
type Result = Unwrap<typeof maybe1>; // number

위의 예제에서 Unwrap 타입은 Monad 타입의 값을 추론하여 그 내부의 값을 반환하는 타입입니다. 이를 통해 모나드의 내부 값을 추론할 수 있습니다. 다음은 타입스크립트에서 IO 모나드의 예제입니다.

class IO<T> {
  constructor(private effect: () => T) {}
  static of<T>(value: T) {
    return new IO(() => value);
  }
  static from<T>(effect: () => T) {
    return new IO(effect);
  }
  flatMap<U>(fn: (value: T) => IO<U>): IO<U> {
    return new IO(() => fn(this.effect()).run());
  }
  unit(value: T): IO<T> {
    return IO.of(value);
  }
  run() {
    return this.effect();
  }
}
const io1 = IO.from(() => {
  console.log("Hello");
  return "Hello";
});
const io2 = io1.flatMap((x) => IO.from(() => x + " World"));
io2.run(); // Hello World

위의 예제에서 IO 모나드는 부수 효과를 캡슐화하는 모나드입니다. flatMap 메소드는 IO 값을 받아서 다른 IO 값을 반환하는 함수를 적용하는 연산입니다. 이를 통해 부수 효과를 체이닝하고 제어할 수 있습니다. 다음은 타입스크립트에서 IO 모나드를 사용하여 부수 효과를 제어하는 예제입니다.

class IO<T> {
  constructor(private effect: () => T) {}
  static of<T>(value: T) {
    return new IO(() => value);
  }
  static from<T>(effect: () => T) {
    return new IO(effect);
  }
  flatMap<U>(fn: (value: T) => IO<U>): IO<U> {
    return new IO(() => fn(this.effect()).run());
  }
  unit(value: T): IO<T> {
    return IO.of(value);
  }
  run() {
    return this.effect();
  }
}
const io1 = IO.from(() => {
  console.log("First effect");
  return "First";
});
const io2 = io1.flatMap((x) =>
  IO.from(() => {
    console.log("Second effect");
    return x + " Second";
  }),
);
const io3 = io2.flatMap((x) =>
  IO.from(() => {
    console.log("Third effect");
    return x + " Third";
  }),
);
io3.run(); // First effect Second effect Third effect

위의 예제에서 IO 모나드는 부수 효과를 캡슐화하고 체이닝합니다. flatMap 메소드를 사용하면 부수 효과를 순차적으로 실행할 수 있습니다. 이를 통해 부수 효과의 실행 순서를 제어할 수 있습니다. flatMap 메소드는 모나드 값을 받아서 다른 모나드 값을 반환하는 함수를 적용하는 연산입니다. 이를 통해 모나드 값을 변환하고 체이닝할 수 있습니다. map 메소드는 이터러블 객체의 각 요소에 대해 함수를 적용하여 새로운 이터러블 객체를 생성하는 연산입니다. forEach 메소드는 이터러블 객체의 각 요소에 대해 함수를 적용하지만 새로운 이터러블 객체를 생성하지 않습니다.

flatMap 메소드와 map, forEach 메소드의 차이점

  1. 적용 대상: flatMap 메소드는 모나드 값에 적용되는 반면, map, forEach 메소드는 이터러블 객체에 적용됩니다.
  2. 반환 값: flatMap 메소드는 모나드 값을 반환하는 반면, map 메소드는 이터러블 객체를 반환하고 forEach 메소드는 반환값이 없습니다.
  3. 체이닝: flatMap 메소드는 모나드 값을 체이닝하는 데 사용됩니다. map, forEach 메소드는 이터러블 객체의 각 요소에 대해 함수를 적용하는 데 사용됩니다.

모나드는 타입스크립트에서 기본적으로 정의된 타입이 아닙니다. 모나드는 함수형 프로그래밍에서 사용되는 디자인 패턴으로, 개발자가 직접 구현해야 합니다. 모나드는 flatMapunit이라는 두 가지 메소드를 가지고 있어야 하며, 이러한 메소드를 사용하여 값을 변환하고 체이닝하는 방식으로 작동합니다. 따라서 모나드는 사용하기로 약속된 규범이라고 볼 수 있습니다. 모나드라는 용어가 코드에 사용되면 다른 개발자들은 해당 타입이 flatMapunit 메소드를 가지고 있을 것이라고 예상할 수 있습니다. 이는 모나드가 함수형 프로그래밍에서 널리 사용되는 디자인 패턴이기 때문입니다.

모나드의 인터페이스나 추상 클래스를 제공하는 타입 라이브러리는 존재하지 않습니다. 하지만 타입스크립트에서는 인터페이스를 사용하여 모나드가 가져야 할 메소드를 정의할 수 있습니다. 또한, 모나드를 구현한 모듈을 작성하여 임포트해서 사용할 수도 있습니다.

interface Monad<T> {
  flatMap<U>(fn: (value: T) => Monad<U>): Monad<U>;
  unit(value: T): Monad<T>;
}
class Maybe<T> implements Monad<T> {
  constructor(private value: T | null) {}
  static of<T>(value: T | null) {
    return new Maybe(value);
  }
  flatMap<U>(fn: (value: T) => Maybe<U>): Maybe<U> {
    return this.value === null ? Maybe.of(null) : fn(this.value);
  }
  unit(value: T): Maybe<T> {
    return Maybe.of(value);
  }
}

위의 예제에서 Monad 인터페이스는 모나드가 가져야 할 메소드를 정의합니다. Maybe 클래스는 Monad 인터페이스를 구현하여 flatMapunit 메소드를 정의합니다.

타입스크립트의 미리 정의된 유틸리티 타입 중에는 모나드의 구현체가 없습니다. 모나드는 함수형 프로그래밍에서 사용되는 디자인 패턴으로, 개발자가 직접 구현해야 합니다. 타입스크립트에서는 인터페이스와 제네릭을 사용하여 모나드를 구현할 수 있습니다.

자바스크립트의 객체는 불변성을 가지고 있지 않습니다. 이는 객체의 속성이 변경될 수 있다는 것을 의미합니다. 이러한 특성은 모나드의 구현에 문제를 일으킬 수 있습니다. 모나드는 함수형 프로그래밍에서 사용되는 디자인 패턴으로, 순수 함수와 불변성을 강조합니다. 따라서 모나드를 사용할 때는 객체의 불변성을 유지하는 것이 중요합니다. 자바스크립트에서는 Object.freeze 메소드를 사용하여 객체의 불변성을 유지할 수 있습니다.

const obj = { x: 3 };
Object.freeze(obj);
obj.x = 4; // TypeError: Cannot assign to read only property 'x' of object '#<Object>'

위의 예제에서 Object.freeze 메소드를 사용하여 객체의 속성을 변경할 수 없도록 합니다. 이를 통해 객체의 불변성을 유지할 수 있습니다. 다음은 자바스크립트에서 Object.freeze 메소드를 사용하는 예제입니다.

const obj = { x: 3, y: { z: 5 } };
Object.freeze(obj);
obj.x = 4; // TypeError: Cannot assign to read only property 'x' of object '#<Object>'
obj.y.z = 6; // No error
console.log(obj); // { x: 3, y: { z: 6 } }

위의 예제에서 Object.freeze 메소드를 사용하여 객체의 속성을 변경할 수 없도록 합니다. 하지만 Object.freeze 메소드는 얕은 동결(shallow freeze)만 수행하기 때문에 중첩된 객체의 속성은 변경할 수 있습니다.

const obj = { x: 3, y: { z: 5 } };
Object.freeze(obj);
Object.freeze(obj.y);
obj.x = 4; // TypeError: Cannot assign to read only property 'x' of object '#<Object>'
obj.y.z = 6; // TypeError: Cannot assign to read only property 'z' of object '#<Object>'
console.log(obj); // { x: 3, y: { z: 5 } }

위의 예제에서 Object.freeze 메소드를 사용하여 중첩된 객체의 속성도 변경할 수 없도록 합니다. 이를 통해 객체의 불변성을 유지할 수 있습니다. Object.freeze 메소드를 사용하여 enum과 유사한 기능을 하는 객체를 생성할 수 있습니다. Object.freeze 메소드를 사용하면 객체의 속성을 변경할 수 없도록 할 수 있기 때문입니다.

const Color = Object.freeze({
  RED: "red",
  GREEN: "green",
  BLUE: "blue",
});
console.log(Color.RED); // red
Color.RED = "yellow"; // TypeError: Cannot assign to read only property 'RED' of object '#<Object>'

위의 예제에서 Object.freeze 메소드를 사용하여 Color 객체의 속성을 변경할 수 없도록 합니다. 이를 통해 enum과 유사한 기능을 하는 객체를 생성할 수 있습니다. immer.jslodash.js는 모나드의 구현과 직접적인 관련이 없습니다. 하지만 이러한 라이브러리를 사용하면 모나드를 사용하는 코드의 가독성과 사용성을 높일 수 있습니다.

import produce from "immer";
const baseState = [
  { task: "Learn typescript", done: true },
  { task: "Try immer", done: false },
];
const nextState = produce(baseState, (draftState) => {
  draftState.push({ task: "Tweet about it" });
  draftState[1].done = true;
});
console.log(baseState); // [{ task: 'Learn typescript', done: true }, { task: 'Try immer', done: false }]
console.log(nextState); // [{ task: 'Learn typescript', done: true }, { task: 'Try immer', done: true }, { task: 'Tweet about it' }]

immer.js는 불변성을 유지하는 데이터 구조를 제공하는 라이브러리입니다. immer.js를 사용하면 모나드를 사용하는 코드에서 불변성을 쉽게 유지할 수 있습니다. 위의 예제에서 produce 함수를 사용하여 baseState 객체의 불변성을 유지하면서 새로운 상태를 생성합니다.

import _ from "lodash";
const users = [
  { user: "barney", age: 36, active: true },
  { user: "fred", age: 40, active: false },
  { user: "pebbles", age: 1, active: true },
];
const result = _.filter(users, (o) => !o.active);
console.log(result); // [{ user: 'fred', age: 40, active: false }]

lodash.js는 유틸리티 함수를 제공하는 라이브러리입니다. lodash.js를 사용하면 모나드를 사용하는 코드에서 데이터 변환을 쉽게 할 수 있습니다. 위의 예제에서 filter 함수를 사용하여 users 배열에서 active 속성이 false인 요소만 추출합니다.

· 약 7분
Dongmin Yu

노코드/로우코드 프로토타이핑/와이어프레이밍 예시

웹이나 모바일 앱의 컴포넌트들을 구성하고 배치해 볼 수 있는 프로토타이핑/와이어프레이밍 툴로는 웹플로우(Webflow)가 있습니다. 웹플로우는 코딩 없이 자유도 높은 프로토타입을 만들 수 있는 노코드 웹 빌더입니다. 다른 노코드 툴들도 있습니다. 예를 들어 Glide와 Adalo는 모바일 앱을 만들 수 있는 노코드 툴입니다. 이 외에도 다양한 노코드 툴들이 있습니다.

References

  1. inflearn.com
  2. tkim.co
  3. inflearn.com

Framer, ProtoPie, Bubble, Retool

Framer와 ProtoPie는 프로토타이핑 툴입니다. Bubble과 Retool은 노코드 툴로 웹 애플리케이션을 만들 수 있습니다. Bubble은 코딩 없이 웹 애플리케이션을 만들 수 있고, Retool은 데이터베이스와 API를 연결하여 내부 비즈니스 앱을 빠르게 만들 수 있는 툴입니다Bubble vs Retool.

References

  1. rosettadigital.com
  2. sourceforge.net
  3. sourceforge.net+1

Framer와 ProtoPie의 차이점

Framer와 ProtoPie는 모두 프로토타이핑 툴입니다. ProtoPie는 완전히 코딩 없는 프로토타입 툴로 디자이너들이 쉽게 사용할 수 있습니다. 다양한 기능과 통합 기능을 제공하며 고급 협업 기능을 갖추고 있습니다. Framer는 코딩을 알고 있는 디자인 팀에게 훌륭합니다. 그러나 Framer는 다른 프로토타이핑 툴에 비해 학습 곡선이 높으며 기술적 배경이 부족한 디자이너들에게 이상적이지 않습니다.

References

  1. protopie.io
  2. g2.com
  3. stackshare.io

Framer의 장점

Framer는 디자인과 코드를 모두 사용하여 모바일 및 웹 인터페이스의 고품질 프로토타입을 만들 수 있습니다. 새로운 프로젝트를 시작할 때 Photoshop에서 자산을 생성하고 스프링 물리학과 베지어 곡선 덕분에 3D 공간에서 레이어를 애니메이션화하며 CSS 변환을 기반으로 하드웨어 가속을 사용하여 높은 프레임 속도를 달성할 수 있습니다.

References

  1. framer.com
  2. webruden.tistory.com
  3. blog.naver.com+1

ProtoPie의 장점

ProtoPie는 디자이너들이 상호작용 디자인 아이디어를 현실적인 프로토타입으로 쉽게 만들 수 있는 도구입니다. 스마트폰과 데스크탑부터 TV와 자동차 대시보드 화면까지 실제 디스플레이에서 내일의 디지털 경험을 만들고 테스트할 수 있습니다. ProtoPie를 사용하면 마이크로 상호작용부터 복잡한 다중 화면 애니메이션까지 모든 상호작용을 탐색할 수 있습니다.

References

  1. protopie.io
  2. protopie.io
  3. protopie.io

ProtoPie의 단점

ProtoPie는 매우 강력한 프로토타이핑 툴이지만 모든 툴과 마찬가지로 몇 가지 단점이 있을 수 있습니다. ProtoPie의 단점 중 하나는 다른 프로토타이핑 툴에 비해 학습 곡선이 높을 수 있다는 것입니다. 또한 ProtoPie는 다른 디자인 툴과의 통합이 제한적일 수 있습니다. 하지만 이러한 단점들은 개인적인 선호도와 작업 스타일에 따라 다를 수 있습니다.

Framer의 단점

Framer는 매우 강력한 프로토타이핑 툴이지만 모든 툴과 마찬가지로 몇 가지 단점이 있을 수 있습니다. Framer의 단점 중 하나는 다른 프로토타이핑 툴에 비해 학습 곡선이 높을 수 있다는 것입니다. 또한 Framer는 코딩 지식이 없는 디자이너들에게 이상적이지 않을 수 있습니다. 하지만 이러한 단점들은 개인적인 선호도와 작업 스타일에 따라 다를 수 있습니다.

리액트네이티브 개발자가 개발하려는 앱의 프로토타이핑을 하려 할 때, 두 도구를 모두 사용해 본 적이 없다면 어떤 것을 사용하는 것이 더 개발자 경험과 결과물의 퀄리티 측면에서 좋습니까?

Framer와 ProtoPie 모두 리액트 네이티브 개발자가 개발하려는 앱의 프로토타이핑을 위해 사용할 수 있는 강력한 툴입니다. Framer는 코딩 지식이 있는 디자인 팀에게 훌륭합니다. 그러나 Framer는 다른 프로토타이핑 툴에 비해 학습 곡선이 높으며 기술적 배경이 부족한 디자이너들에게 이상적이지 않을 수 있습니다. 반면 ProtoPie는 완전히 코딩 없는 프로토타입 툴로 디자이너들이 쉽게 사용할 수 있습니다. 따라서 리액트 네이티브 개발자가 개발하려는 앱의 프로토타이핑을 위해 어떤 툴을 사용할지 결정하기 전에 개인적인 선호도와 작업 스타일을 고려해야 합니다.

· 약 7분
Dongmin Yu

[] (c) 2003 The FreeBSD Documentation Project.

[] The FreeBSD Documentation Project is a member of the FreeBSD Foundation.

NAME

builtin, !, %, ., :, @, \[, {, }, alias, alloc, bg, bind, bindkey, break, breaksw, builtins, case, cd, chdir, command, complete,
continue, default, dirs, do, done, echo, echotc, elif, else, end, endif, endsw, esac, eval, exec, exit, export, false, fc, fg,
filetest, fi, for, foreach, getopts, glob, goto, hash, hashstat, history, hup, if, jobid, jobs, kill, limit, local, log, login,
logout, ls-F, nice, nohup, notify, onintr, popd, printenv, printf, pushd, pwd, read, readonly, rehash, repeat, return, sched, set,
setenv, settc, setty, setvar, shift, source, stop, suspend, switch, telltc, test, then, time, times, trap, true, type, ulimit,
umask, unalias, uncomplete, unhash, unlimit, unset, unsetenv, until, wait, where, which, while – shell built-in commands

SYNOPSIS

See the built-in command description in the appropriate shell manual page.

DESCRIPTION

Shell builtin commands are commands that can be executed within the running shell's process. Note that, in the case of csh(1) builtin commands, the command is executed in a subshell if it occurs as any component of a pipeline except the last.

If a command specified to the shell contains a slash /, the shell will not execute a builtin command, even if the last component. of the specified command matches the name of a builtin command. Thus, while specifying “echo” causes a builtin command to be executed under shells that support the echo builtin command, specifying “/bin/echo” or “./echo” does not.

While some builtin commands may exist in more than one shell, their operation may be different under each shell which supports them. Below is a table which lists shell builtin commands, the standard shells that support them and whether they exist as standalone utilities.

Only builtin commands for the csh(1) and sh(1) shells are listed here. Consult a shell's manual page for details on the operation of its builtin commands. Beware that the sh(1) manual page, at least, calls some of these commands “built-in commands” and some of them “reserved words”. Users of other shells may need to consult an info(1) page or other sources of documentation.

Commands marked “No**” under External do exist externally, but are implemented as scripts using a builtin command of the same name.

commandexternalcshsh
!NoNoYes
%NoYesNo
.NoNoYes
:NoYesYes
@NoYesYes
[YesNoYes
{NoNoYes
}NoNoYes
aliasNo**YesYes
allocNoYesNo
bgNo**YesYes
bindNoNoYes
bindkeyNoYesNo
breakNoYesYes
breakswNoYesNo
builtinNoNoYes
builtinsNoYesNo
caseNoYesYes
cdNo**YesYes
chdirNoYesYes
commandNo**NoYes
completeNoYesNo
continueNoYesYes
defaultNoYesNo
dirsNoYesNo
doNoNoYes
doneNoNoYes
echoYesYesYes
echotcNoYesNo
elifNoNoYes
elseNoYesYes
endNoYesNo
endifNoYesNo
endswNoYesNo
esacNoNoYes
evalNoYesYes
execNoYesYes
exitNoYesYes
exportNoNoYes
falseYesNoYes
fcNo**NoYes
fgNo**YesYes
filetestNoYesNo
fiNoNoYes
forNoNoYes
foreachNoYesNo
getoptsNo**NoYes
globNoYesNo
gotoNoYesNo
hashNo**NoYes
hashstatNoYesNo
historyNoYesNo
hupNoYesNo
ifNoYesYes
jobidNoNoYes
jobsNo**YesYes
killYesYesYes
limitNoYesNo
localNoNoYes
logNoYesNo
loginYesYesNo
logoutNoYesNo
ls-FNoYesNo
popdNoYesNo
printenvYesYesNo
printfYesNoYes
pushdNoYesNo
pwdYesNoYes
readNo**NoYes
readonlyNoNoYes
rehashNoYesNo
repeatNoYesNo
returnNoNoYes
schedNoYesNo
setNoYesYes
setenvNoYesNo
settcNoYesNo
settyNoYesNo
setvarNoNoYes
shiftNoYesYes
sourceNoYesNo
stopNoYesNo
suspendNoYesNo
switchNoYesNo
telltcNoYesNo
testYesNoYes
thenNoNoYes
timeYesYesNo
timesNoNoYes
trapNoNoYes
trueYesNoYes
typeNo**NoYes
ulimitNo**NoYes
umaskNo**YesYes
unaliasNo**YesYes
uncompleteNoYesNo
unhashNoYesNo
unlimitNoYesNo
unsetNoYesYes
unsetenvNoYesNo
untilNoNoYes
waitNo**YesYes
whereNoYesNo
whichYesYesNo
whileNoYesYes

SEE ALSO

csh(1), dash(1), echo(1), false(1), info(1), kill(1), login(1), nice(1), nohup(1), printenv(1), printf(1), pwd(1), sh(1), test(1), time(1), true(1), which(1), zsh(1)

HISTORY

The builtin manual page first appeared in FreeBSD 3.4.

AUTHORS

This manual page was written by Sheldon Hearn.

 macOS 13.3 December 21, 2010 m

· 약 2분
Dongmin Yu

Git 자격 증명에 로그인하려면 다음 단계를 따르세요:

  1. 컴퓨터에서 (Git) Bash 또는 명령 프롬프트를 엽니다.
  2. git config --global credential.helper store를 입력하고 Enter를 누릅니다. 로그인 자격 증명을 저장하도록 GIT가 구성됩니다.
  3. git config --global user.name를 입력하고 Enter를 누릅니다. GIT 사용자 이름으로 교체하십시오.
  4. git config --global user.email를 입력하고 Enter를 누릅니다. GIT 계정과 관련된 이메일 주소로 교체하십시오.
  5. git config --global credential.username를 입력하고 Enter를 누릅니다. 이렇게하면 GIT 사용자 이름을 GIT 자격 증명의 기본 사용자 이름으로 설정합니다.
  6. git config --global credential.helper 'cache --timeout=3600'를 입력하고 Enter를 누릅니다. 이로 인해 GIT 자격 증명에 대한 시간 초과 1 시간이 설정됩니다.

이 단계를 완료하면 Git 사용자 이름과 비밀번호를 사용하여 Git 자격 증명에 로그인할 수 있습니다.