Skip to main content

youtube-dl로 유튜브 무료 다운로드하기

· 5 min read

유튜브, 비메오 등의 영상을 소장하고 싶거나 좋은 노래를 다운받아서 듣고 싶을 때 많은 고민을 하게 된다 Youtube Free Download 같은 사이트에서 광고를 한참 보다가 한개씩 다운받아야하고, 영상일 경우 저화질만 받을 수 있다

이 고민거리를 한 방에 해결해주는 Youtube-DL(DownLoad)이 있다 시작해보자.

설치

커맨드와 매뉴얼 설치방법으로 나눴다.

윈도우

scoop 설치

쉽게 설치하기 위해서 윈도우 패키지 매니저인 Scoop을 먼저 설치하자. 시작표시줄 우측 클릭 후 속성에 들어가면 win + x 키를 누를 때 관리자 파워쉘로 접속할 수 있는 설정이 있다. 설정을 체크해주고, win + xa 키를 누르면 관리자 파워쉘로 접근한다.

PS > iex (new-object net.webclient).downloadstring('https://get.scoop.sh')

패키지 설치

## ffmpeg 은 음악, 영상 파일 포맷 변환기이다.
$ scoop install ffmpeg youtube-dl

수동 설치

위에 커맨드가 너무 어렵다면 매뉴얼대로 설치해보자. 여기를 누르면 다운받는다 (사실 이게 더 쉽지만 쉘로 설치하는게 뭔가 나이스하니까)

비디오를 오디오로 변경해주기위해 ffmpeg를 다운받아야한다. 링크를 따라가서 Download Build 버튼을 클릭해 압축을 풀어주자

ffmpeg/bin 안에 youtube-dl.exe 를 넣어준다

환경 변수 설정

  1. Win+R > sysdm.cpl
  2. 고급 > 환경 변수 > 시스템 변수 > Path 선택 > 편집 > 새로만들기 에서 ffmpeg/bin 경로를 넣어준다.
  3. D:\ffmpeg\bin
  4. 확인 ✔️ X 3

Win+R > cmd를 친 뒤에 테스트를 해본다, 아니면 Win+x > cmd (or Powershell) 한 다음 열어도 된다.

## ffmpeg 세팅 확인
$ ffmpeg -version

ffmpeg version N-90433-g5b31dd1c6b Copyright (c) 2000-2018 the FFmpeg developers
built with gcc 7.3.0 (GCC)

## youtube-dl 세팅 확인
$ youtube-dl --version

2018.03.14

## brew 로 한 번에 설치가 가능하다.
$ brew install youtube-dl

사용법

mp4 다운로드

$ youtube-dl 주소

## mp4 best quality로 다운받고 싶다면
$ youtube-dl 주소 -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best'

## 4K 영상을 다운받고 싶다면
$ youtube-dl 주소 -f '(bestvideo[vcodec=vp9]/bestvideo[ext=mp4]/bestvideo)+bestaudio/best'

## 비공개 비밀번호 입력 영상이라면
$ youtube-dl 주소 --video-password 비밀번호

## 로그인하고 받아야되는 영상이라면
$ youtube-dl 주소 -u 아이디 -p 비밀번호

mp3 다운로드

## audio-quality는 기본값이 5고 0으로 갈수록 좋은 음질
$ youtube-dl 주소 -x --audio-format mp3 --audio-quality 0

예시

트위터의 경우 동영상 주소 복사 버튼을 클릭하면 나오는 주소로도 다운로드가 가능하다.

## 영상 다운로드
$ youtube-dl https://www.youtube.com/watch?v=IlSY06XGu3Y

## mp3 다운로드 일반음질 (128K)
$ youtube-dl https://www.youtube.com/watch?v=nfs8NYg7yQM -x --audio-format mp3

## mp3 다운로드 고음질 (VBR)
$ youtube-dl https://www.youtube.com/watch?v=nfs8NYg7yQM -x --audio-format mp3 --audio-quality 0

버전 업데이트

명령어 하나로 셀프 업데이트가 가능하다.

## 다운로드 오류 발생시 업데이트하면 해결된다.
$ youtube-dl -U

지원하는 사이트

유튜브 말고 수많은 사이트의 리소스 다운이 가능하다. 사이트 목록은 여기서 확인 가능하다.

웹팩이 모듈을 불러오는 슈도코드

· 15 min read

잊기 전에 슈도코드를 정리해놓자.

슈도코드

var 전체모듈 = [
function () {
const 합계함수 = (a, b) => a + b;
return 합계함수;
},

function () {
const 내부_합계함수 = 전체모듈[0]();
const 합계 = 내부_합계함수(10, 20);
console.log(합계);
return 합계;
},
];

const 시작모듈_인덱스 = 1;
전체모듈[시작모듈_인덱스]();

해석

배열에 다 때려넣고 호출해서 사용하는 방법이다 물론 내부는 더 복잡하다, 코드 스플리팅이 된다면 더더욱.

복잡한 내부

https://github.com/hg-pyun/minipack-kr/blob/master/src/minipack.js
/**
* @source https://github.com/hg-pyun/minipack-kr/blob/master/src/minipack.js
*
* 모듈 번들러들은 작은 코드 조각들을 웹 브라우저에서 실행될 수 있는 크고 복잡한 파일로 컴파일합니다.
* 이 작은 조각들은 단지 자바스크립트 파일들일 뿐이며, 이들 사이의 종속성은 모듈 시스템에 의해 표현됩니다
* (https://webpack.js.org/concepts/modules).
*
* 모듈 번들러들은 entry file 이라는 개념을 가지고 있습니다. 브라우저에 스크립트 태그를 몇개 추가하여
* 실행하는 대신, 번들 담당자에게 응용 프로그램의 메인 파일이 무엇인지 알려 줍니다. 이 파일이 어플리케이션을
* 실행하는 진입점이 됩니다.
*
* 번들러는 entry file의 의존성을 분석합니다. 그리고 그 다음 파일의 의존성을 파악합니다.
* 이 작업은 애플리케이션의 모든 모듈과 각 모듈이 서로 어떻게 의존하는지 파악할 때까지 반복됩니다.
*
* 이러한 프로젝트에 대한 이해를 종속성 그래프라 부릅니다.
*
* 이 예제에서는 종속성 그래프를 만들고 이 그래프를 사용하여 모든 모듈들을 하나의 번들로 패키징 합니다.
* 그럼 시작해 보겠습니다 :)
*
* 참고: 이 예제는 매우 단순화되어 있습니다. 순환 참조, 캐싱 모듈, 파싱 최적화 등에 대한 내용은 생략
* 하여 가능한가 단순하게 만들었습니다.
*/

const fs = require("fs");
const path = require("path");
const babylon = require("babylon");
const traverse = require("babel-traverse").default;
const { transformFromAst } = require("babel-core");

let ID = 0;

// 우선 file path를 받는 함수를 생성하고
// 파일을 내용을 읽고, 종속성을 추출합니다.
function createAsset(filename) {
// 파일의 내용을 문자열로 읽습니다.
const content = fs.readFileSync(filename, "utf-8");

// 이제 이 파일이 어떤 파일에 종속되는지 알아보겠습니다. 우리는 import 문자열을 보고 의존성을
// 파악할 수 있습니다 하지만, 이것은 단순한 접근법이어서, 대신에 자바스크립트 파서를 사용하겠습니다.

// 자바스크립트 파서들은 자바스크립트 코드를 읽고 이해할 수 있도록 도와주는 툴입니다.
// 파서는 AST(abstract syntax tree)라는 좀더 추상화된 모델을 생성합니다.
//
// AST에 대해 이해하려면 AST Explorer(https://astexplorer.net)을 꼭 보기를 강력하게 추천합니다.
// AST가 어떻게 이루어져 있는지 확인할 수 있습니다.
//
// AST는 우리의 코드에 대해 많은 정보를 가지고 있습니다. 우리는 쿼리를 이용하여
// 우리의 코드가 하려는 일에 대해 이해할 수 있습니다.
const ast = babylon.parse(content, {
sourceType: "module",
});

// 이 배열은 현재 모듈의 의존성을 상대 경로로 가지고 있을 것입니다.
const dependencies = [];

// 우리는 AST 순회를 통해 각각의 모듈들이 어떤 의존성을 가지고 있는지 이해하려 합니다.
// 이것을 통해 AST안에서 모든 import keyword 선언을 파악할 수 있습니다.
traverse(ast, {
// ECMAScript 모듈들은 정적이므로 매우 파악하기 쉽습니다.이는 변수를 가져올 수 없거나 조건부로
// 다른 모듈을 가져올 수 없음을 의미합니다. import 구분을 볼 때 마다 카운팅을 하고 의존성을 가지고
// 있는 것으로 간주 할 수 있습니다.
ImportDeclaration: ({ node }) => {
// import 구문마다 dependencies 배열에 값을 추가합니다.
dependencies.push(node.source.value);
},
});

// 또한 간단한 카운터를 이용하여 이 모듈에 고유 식별자를 할당합니다.
const id = ID++;

// 우리는 일부 브라우저에서만 지원하는 ECMAScript module들이나 기능들을 사용할 가능성도 있습니다.
// 우리가 만드는 번들이 모든 브라우저에서 돌아가도록 Babel을 이용해서 transpile할 수 있습니다
// (https://babeljs.io 참고).
//
// `presets` 옵션은 Babel이 어떻게 우리 코드를 바꿀지에 대해 결정합니다. 우리는 `babel-preset-env`
// 를 이용하여 대부분의 브라우저에서 우리의 코드를 사용할 수 있도록 바꾸도록 하겠습니다.
const { code } = transformFromAst(ast, null, {
presets: ["env"],
});

// 이 모듈에 대한 정보를 return 합니다.
return {
id,
filename,
dependencies,
code,
};
}

// 이제 단일 모듈의 종속성을 추출할 수 있으므로, entry file의 의존성을 추출하는 것부터 시작하겠습니다.
// 이 작업은 애플리케이션의 모든 모듈과 각 모듈이 서로 어떻게 의존하는지를 파악할 때까지 계속 진행할 것입니다.
// 이 작업의 결과물을 의존성 그래프라 부릅니다.
function createGraph(entry) {
// entry file부터 분석을 시작합니다.
const mainAsset = createAsset(entry);

// queue를 사용해서 모든 asset의 의존성을 분석하도록 하겠습니다. 이 작업을 위해
// entry asset을 가지고 있는 배열을 정의합니다.
const queue = [mainAsset];

// 여기서 queue의 반복을 위해 `for ... of` 반복문을 사용합니다. 처음에는 queue가 asset을 하나만
// 가지고 있지만 작업이 반복되는 동안에 새로운 asset들을 queue에 추가합니다. 이 반복문은 queue가
// 비어질 때 까지 계속됩니다.
for (const asset of queue) {
// 모든 asset들은 의존성이 있는 모듈에 대한 상대경로들을 리스트로 가지고 있습니다. 우리는 그 리스트를
// 순회하면서 `createAsset()`함수로 분석하고, 아래 객체를 통하여 모듈들의 의존성을 추척할 것입니다.
asset.mapping = {};

// 이것은 이 모듈이 있는 디렉토리입니다.
const dirname = path.dirname(asset.filename);

// 종속성에 대한 상대 경로 리스트를 순회합니다.
asset.dependencies.forEach((relativePath) => {
// `createAsset()` 함수는 절대 경로가 필요합니다. dependencies 배열은 상대 경로를 가지고
// 있는 배열입니다. 이러한 경로들은 모듈이 import된 file에 따라 상대적입니다. 따라서 부모 asset의
// 경로를 이용해서 상대 경로를 절대경로로 바꿔야 합니다.
const absolutePath = path.join(dirname, relativePath);

// asset의 내용울 분석하고, 내용을 읽고, 의존성을 추출합니다.
const child = createAsset(absolutePath);

// `asset`의 의존성은 `child`에게 달려있습니다. 우리는 `mapping` 객체에 relativePath와 child.id를
// 이용해서 관계를 표현할 수 있습니다.
asset.mapping[relativePath] = child.id;

// 마지막으로 child asset을 queue에 추가하여 구문 분석이 반복되도록 합니다.
queue.push(child);
});
}

// 이 시점에서 queue는 애플리케이션의 모든 모듈이 포함된 배열입니다. 이것이 우리가 그래프를 표현하는 방법입니다.
return queue;
}

// 다음으로, 그래프를 이용하여 브라우저에서 실행할 수 있는 번들을 반환하는 함수를 정의합니다.
//
// 우리의 번들은 self-invoking(자신을 부를수 있는)함수를 가지고 있습니다.
//
// (function() {})()
//
// 이 함수는 하나의 인자만 받을 수 있습니다: 모든 모듈의 정보를 가지고 있는 그래프.
function bundle(graph) {
let modules = "";

// 이 함수를 구성하기 전에 매개 변수로 전달할 객체를 만들겠습니다. 반드시 알아둬야할 것은 우리가 만드는
// 스트링은 2개의 중괄호({})로 감싸져 있어야 한다는 것입니다. 우리는 다음과 같은 포멧으로 추가할
// 것입니다: `key: value,`.
graph.forEach((mod) => {
// 그래프안에 있는 모든 모듈들은 entry를 객체로 가지고 있습니다. 우리는 module의 id를
// 값에 대한 키로 사용합니다.(각 모듈마다 2개의 값이 있습니다.)
//
// 찻번째 값은 함수로 감싼 각 모듈의 코드입니다. 그 이유는 모듈의 scope를 지정해야 하기 때문입니다.
// 한 모듈에서 변수를 정의하면 다른 모듈이나 글로벌 scope에 영향을 주지 않아야 합니다.
//
// transpiled된 모듈들은 CommonJS 모듈 시스템을 사용합니다:
// 해당 모듈 시스템은 `require`, `module`, 그리고 `exports`를 통해 모듈화 합니다.
// 이 키워드들은 일반적으로 브라우저에서 사용할수 없으므로, 우리의 함수를 이용하여 주입해야 합니다.
//
// 두번째 값은 모듈간의 의존성 매핑을 stringify하는 것입니다. 다음과 같은 객체입니다.
// { './relative/path': 1 }.
//
// transpiled된 우리의 모듈들이 상대경로와 합께 `require()`를 호출하기 때문입니다. 이 함수를 호출하면
// 그래프에서 이 모듈의 상대 경로에 해당하는 모듈을 확인할 수 있습니다.
modules += `${mod.id}: [
function (require, module, exports) { ${mod.code} },
${JSON.stringify(mod.mapping)},
],`;
});

// 마지막으로 self-invoking 함수의 body를 만듭니다.
//
// `require()` 함수를 만들며 시작하겠습니다: 모듈 id를 받아 앞서 만든 모듈 오브젝트에서 `module`을
// 찾습니다. function wrapper와 맵핑 객체를 얻기위해 two-value 객체를 이용합니다.
//
// 모듈의 코드는 모듈의 id들 대신 상대경로와 함께 `require()`함수를 호출합니다. 우리가 만든 require 함수는
// id 받습니다. 또한 두개의 모듈은 동일한 상대 경로를 요구할 수 있지만, 실제론 두개의 다른 모듈들을
// 의미하게 됩니다.
//
// 이 문제를 해결하기 위해 별도의 `require` 함수를 제공합니다. 모듈의 맵핑 오브젝트를 이용하여 상대경로를 ids에 할당합니다.
// 맵핑 오브젝트는 구체적인 모듈을 가져오기 위한 용도로, 상대 경로와 모듈 ids를 맵핑합니다.
//
// 마지막으로, 모듈이 require 되었을 때 exports 객체의 값이 노출되어야 합니다. 따라서 모듈 코드에 의해 변환 된
// `exports` 객체는 `require()`로 반환됩니다.
const result = `
(function(modules) {
function require(id) {
const [fn, mapping] = modules[id];
function localRequire(name) {
return require(mapping[name]);
}
const module = { exports : {} };
fn(localRequire, module, module.exports);
return module.exports;
}
require(0);
})({${modules}})
`;

// 결과를 반환합니다. 만세! :)
return result;
}

const graph = createGraph("./example/entry.js");
const result = bundle(graph);

console.log(result);

여기의 번역된 내용을 확인해보자.

나를 위한 면접 (Backend Developer Interview Questions)

· 27 min read

BE Interview Questions를 아는 만큼 답해보았다 지극히 주관적이라 정답이 아닐 수 있습니다

Design Patterns

Why are global and static objects evil?

  • 전역변수는 어디서 변경되었는지 추적이 힘듦
  • 그리고 자바의 경우 콜스택이 끝나면 비워지는 지역변수와 달리 메모리의 static 영역에 계속 상주해 있음

Tell me about Inversion of Control and how does it improve the design of code

  • 클래스 안에 다른 클래스의 기능이 필요하다면 내부에서 생성자를 이용해 두 클래스 간의 종속이 생겨버리는데
  • IoC를 통한다면 DI 패턴으로 구현해 new A(new B()) 처럼 종속성을 분리시킬 수 있음
  • TypeHint를 통해 의존성을 확인할 수 있으므로 더 직관적이기도 하다
  • What is Inversion of Control?

Active-Record is the design pattern that promotes objects to include functions such as Insert, Update, and Delete, and properties that correspond to the columns in some underlying database table. In your opinion and experience, which are the limits and pitfalls of the this pattern?

  • 장점
    • 하나의 구조로 모든 모델을 제어할 수 있게 해준다.
    • 또한 모든 DB 마다 다른 쿼리문 (예를 들어 페이징)을 손쉽게 처리할 수도 있다.
    • 무엇보다 엄청 빠르게 DB 조작이 가능해진다.
  • 단점
    • DB를 잊어버리게 되는 것 아닐까? ActiveRecord로 개발을 시작하는 건 아니다라고 본다.
    • 여러 테이블의 조인이 힘들고 (조인 조건을 만족하기 위해) 계산이 들어간 쿼리문이라면 어차피 RAW 쿼리를 날려야한다.
    • 프로시져를 사용할 수도 없고, 관계형 모델들은 select를 키값을 통해 여러 번 select 한 뒤 합쳐주는 것 뿐이라 성능상의 이슈도 있다.
    • 결국 필요한 곳에서만 (RESTful 구조의) 사용해서 개발하는 게 좋을 것 같다

Data-Mapper is a design pattern that promotes the use of a layer of Mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself. On the contrary, in Active-Record objects directly incorporate operations for persisting themselves to a database, and properties corresponding to the underlying database tables. Do you have an opinion on those patterns? When would you use one against the other?

The big benefit of the Data Mapper pattern is, your domain objects don't need to know anything about how they are stored in the database. This means that your objects will be lighter because they don't have to inherit the full ORM, but also there will be a stricter, more formal process for interacting with the database because you can't just call the save() method anywhere in your code

Why it is often said that the introduction of null is a "Billion dollar mistake"? Would you discuss the techniques to avoid it, such as the Null Object Pattern introduced by the GOF book, or Option types?

  • null 로 변수를 생성하는 건 안하는 거랑 똑같지만, 안 해주면 프로그램이 뻗어버려서 null 타입 체크를 하거나 default값을 추가한다.
  • 10억 달러짜리 실수

Many state that, in Object-Oriented Programming, Composition is often a better option than Inheritance. What's you opinion?

What is an Anti-corruption Layer?

  • 추상화 한 레이어를 하나 더 두고 하위 도메인들을 쉽게 접근할 수 있게 하는 방법, facade 처럼

Singleton is a design pattern that restricts the instantiation of a class to one single object. Writing a Thread-Safe Singleton class is not so obvious. Would you try?

  • Volatile

How would you deal with Dependency Hell?

  • 의존성 컨테이너를 사용한다.
  • 패키지 매니저툴을 이용하며 버전을 명시한다.
  • maven, composer, npm 등등

Is goto evil?

  • 적절하게, 가독성 있게 사용하면 괜찮다.
  • 리눅스 커널 이런 느낌으로

Code Design

It is often heard that one of the most important goals in Object-Oriented Design is to have High Cohesion and Loose Coupling. What does it mean? Why is it that important and how is it achieved?

  • Coupling
    • 모듈 간에 상호 의존하는 정도
    • 각 모듈 간의 결합도가 약해야 하며 의존하는 모듈이 적어야한다.
    • 결합도가 강하면 시스템 구현 및 유지보수 작업이 어렵다.
  • Cohesion
    • 정보 은닉 개념을 확장한 것
    • 명령어나 호출문 등 모듈의 내부 요소들의 서로 관련되어 있는 정도
    • 모듈이 독립적인 기능으로 정의되어 있는 정도
    • 독립적인 모듈이 되기 위해서는 각 모듈의 응집도가 강해야한다

Why does array index start with '0' in most of languages?

  • 배열이 참조하는 메모리의 위치를 나타내므로 (시작 위치에서의 기준점)

Are comments in code useful? Some say they should be avoided as much as possible, and hopefully made unnecessary

  • 변수명, 메소드명으로 명확하게 표현할 수 있으면 좋은데, 그러긴 현실적으로 힘들고
  • JSDoc 같은 구문으로 주석을 단다면 참조하는 다른 소스에서도 hover만으로 모든 설명을 볼 수 있어서도 좋고

What is the difference between design and architecture?

  • Software architecture is more about the design of the entire system, while software design emphasizes on module / component / class level.
  • Architecture: MVC, 3-tier layered design, etc.
  • Design: What are the responsibilities, functions, of module x? Of class Y? What can it do, and what not?
  • Software Design vs. Software Architecture

C++ supports multiple inheritance, and Java allows a class to implement multiple interfaces. What impact does using these facilities have on orthogonality? Is there a difference in impact between using multiple inheritance and multiple interfaces? Is there a difference between using delegation and using inheritance?

  • implements가 좀 더 유연하다 클래스 안에서 직접 구현해야되는거니까

Pros and cons of holding domain logic in Stored Procedures

  • 장점
    • 한 번 만든걸 계속 호출해서 쓸 수 있음
    • 컴파일 언어의 경우 쿼리만 변경하는거니 유지보수가 좋을 수 있음
    • 계산이 들어가는 쿼리의 경우 SP는 캐싱되므로 이만한게 없음
  • 단점
    • DB에 완전 종속됨
    • DBA가 있어야함
    • git에서 변경점 추적 불가능

In your opinion, why have Object-Oriented Design dominated the market for so many years?

  • 관심사의 분리

Languages

Tell me the 3 worse defects of your preferred language

  • PHP
    • 컴파일 언어가 아니라 느림 (PHP7부턴 달라짐, OpCache를 사용하면 되긴함)
    • 쓰레드가 없음 (PHP React나 Guzzle promise 같은 걸 사용하면 event driven 방식으로 되긴 함)
    • 인식이 안 좋음
    • 함수명이 너무 제각각임 (어떤 건 snake_case 어떤건 붙혀서)

Why is there a rising interest on Functional Programming?

  • 각 실행 단계를 이뮤터블로 만들어 Side effect를 없앤다.
  • 테스트가 쉽다.
  • 가독성이 높아진다

What is a closure, and what is useful for? What's in common between closures and classes?

  • FE 인터뷰에 정리해놓았다

What are generics useful for?

  • 타입 캐스팅

What are high-order functions? What are they useful for?

  • 함수를 파라미터로 전달받거나, 함수를 리턴하는 함수로 다형성을 지원해 재사용이 가능하다

What does it mean when a language treats functions as first-class citizens?

  • 변수나 데이터에 할당 가능
  • 파라미터로 넘길 수 있어야함
  • 리턴값으로 리턴이 가능해야함

Show me an example where an Anonymous Function can be useful

  • 모든 콜백에서 유용함

Whats the Stack and what's the Heap? What's a Stack Overflow?

  • Stack: LIFO
  • Heap: 최소 또는 최대 값이 루트에 있는 완전 이진 트리
  • Stack Overflow: 지식인 또는 Stack이 꽉 찼는데 삽입하려 들 때

Some languages, especially the ones that promote a Functional approach, allow a technique called Pattern Matching. Do you know it? How is Pattern Matching different from Switch clauses?

  • C#에서 is 구문을 말하는건가?
  • switch는 하나의 타입에서만 비교가 가능한데, 패턴매칭을 쓰면 더 동적으로 비교가 가능하다

If Cat is an Animal

is TakeCare<Cat> a TakeCare<Animal> ?

  • 같지는 않지만 집어넣을 수는 있다

In the last years there has been a lot of hype on Node. What's your opinion on the use in the back end of a language that was initially conceived to run in the browser?

  • Atwood's Law: any application that can be written in JavaScript, will eventually be written in JavaScript.

참고

Web development

Why first-party cookies and third-party cookies are treated so differently?

  • first-party cookie
  • 자사 쿠키는 방문하는 웹사이트가 설정하며 해당 사이트에서만 사용
  • third-party cookie
  • 타사 쿠키에서 분석 등의 용도로 사용

How would you manage Web Services API versioning?

  • 시맨틱 버저닝

From a Back End perspective, are there any disadvantages or drawbacks on the adoption of Single Page Applications?

  • 백엔드 관점
  • 장점: 웹을 SPA로 가면 API가 필요하고, 디바이스 확장성에 대해 좋음
  • 단점: SEO 때문에 어차피 SSR 해줘야됨

Why do we usually put so much effort for having stateless services? What's so good in stateless code and why and when statefullness is bad?

REST and SOAP: when would you choose one, and when the other?

In Web development, Model-View Controller and Model-View-View-Model approaches are very common, both in the Back End and in the Front End. What are they, and why are they advisable?

  • MVC
    • Controller로 사용자의 입력이 들어옵니다.
    • Controller는 Model을 데이터 업데이트 및 불러오고
    • Model은 해당 데이터를 보여줄 View를 선택해서 화면에 보여주게 됩니다.
  • MVVM
    • View에 입력이 들어오면 Command 패턴으로 ViewModel에 명령을 합니다.
    • ViewModel은 필요한 데이터를 Model에 요청 합니다.
    • Model은 ViewModel에 필요한 데이터를 응답 합니다.
    • ViewModel은 응답 받은 데이터를 가공해서 저장 합니다.
    • View는 ViewModel과의 Data Binding으로 인해 자동으로 갱신 됩니다.
  • MVC, MVP, MVVM 비교

Databases

How would you migrate an application from a database to another, for example from MySQL to PostgreSQL? If you had to manage that project, which issues would you expect to face?

  • Oracle => MySQL
  • date 처리에서 애를 먹었는데, 덤프를 바로 옮기지 않고 데이터를 가져와 다시 넣는 방식으로 마이그레이션 했다
  • Right join (+ 조인)을 Left로 바꾸는데 시간이 걸렸다
  • MySQL => Maria
  • 완벽 호환, utf8mb4로 콜렉션과 차셋도 바꿔줌

Why databases treat null as a so special case? For example, why in SQL SELECT * FROM table WHERE field = null does not match records with null field?

  • 저걸 처리하기 위해선 IS NULL 구문을 사용해야함

ACID is an acronym that refers to Atomicity, Consistency, Isolation and Durability, 4 properties guaranteed by a database transaction in most of the database engines. What do you know about this topic? Would you like to elaborate?

  • 원자성
    • Atomicity
    • 트랜잭션 내의 명령은 반드시 완벽히 수행
    • 모두가 수행되거나 오류시 전부가 취소되어야함
  • 일관성
    • Consistency
    • DB의 전체 요소는 트랜잭션 수행 전과 트랜잭션 수행 완료 후의 상태가 같아야함
  • 독립성
    • Isolation = 격리성 = 순차성
    • 둘 이상의 트랜잭션이 병행 실행되는 경우 다른 트랜잭션 연산이 끼어들 수 없음
    • 수행 중인 트랜잭션은 완료될 때 까지 다른 트랜잭션에서 참조 불가
  • 영속성
    • Durability = 지속성
    • 시스템이 고장나도 영구적으로 반영

How would you manage database schema migrations, that is, how would you automate the changes a database schema is affected to, as the application evolve, version after version?

  • 프레임워크에서 migration 기능을 지원한다면 그걸 사용할 것이다

How is Lazy Loading achieved? When is it useful? What are its pitfalls?

  • 장점: 실제 로직이 실행될 때 로드가 되므로, 자원 소모를 해당 액션이 실행되기 전까지로 미룰 수 있다
  • 단점: N+1 problem.

The so called "N + 1 problem" is an issue that occurs when the code needs to load the children of a parent-child relationship with a ORMs that have lazy-loading enabled, and that therefore issue a query for the parent record, and then one query for each child record. How to fix it?

  • Eager loading을 사용하거나 JPA의 경우 join fetch를 사용한다

In your opinion, is it always needed to use database normalization? When is it advisable to use denormalized databases?

  • 삽입, 삭제, 갱신 이상이 해결된다면, join의 감소를 위해 비정규화도 적절히 필요하다

NoSQL

What is Eventual Consistency?

데이터 삽입이 끝났지만 어떤 클라이언트에서는 업데이트 된 내용을 확인할 수 없다, 하지만 곧 확인할 수 있다

The Brewer's Theorem, most commonly known as the CAP theorem, states that in the presence of a Network Partition (the P in CAP), a system's designer has to choose between Consistency (the C in CAP) and Availability (the A in CAP). Can you think about examples of CP, AP and CA systems?

  • 일관성, 가용성, 분단허용성

  • Consistent - Available : Postgres, MySQL같은 전통적인 RBMS

  • Consistent - Partition Tolerant : BitTable, Hypertable, HBase, MongoDB, Terrastore, Redis, Scalaris, MemcacheDB, BerkeleyDB

  • Available - Partition Tolerant : Amazon Dynamo, Cassandra, CouchDB, Amazon SimpleDB, Riak

  • CAP Theorem, 오해와 진실

  • NoSQL에 대해서 #2

How would you explain the recent rise in interest for NoSQL?

  • Redis

In which case would you use a document database like MongoDB instead of a relational database like MySQL or PostgreSQL?

code versioning

Could you describe GitHub Flow and GitFlow workflows?

  • Git Flow: feature > develop > release > hotfix > master
  • GitHub Flow: feature 별 브랜치 > master, PR

What's a rebase?

  • 여러 브랜치를 하나의 브랜치(커밋)으로 합치기

Concurrency

Why do we need Concurrency, anyway?

  • I/O를 기다리지 않고 다른 일을 처리하고 싶을 때, CPU 코어들을 최대한 쓰고 싶을 때

Why is testing multithreading / concurrent code so difficult?

  • 언제 값이 변경되는지 잡기가 힘들어서

What is a Race Condition? Code an example, using whatever language you like

  • 두 개 이상의 스레드에서 공유된 리소스에 접근할 때 순서에 따라 결과가 달라지는 현상
  • for 문으로도 보여줄 수 있다

What is a Deadlock? Would you be able to write some code that is affected by deadlocks?

  • 이미 리소스를 점유한 스레드의 Lock이 해제될 까지 서로의 스레드가 계속 기다리는 현상

What is Process Starvation? If you need, let's review its definition

  • 우선 순위가 낮은 프로세스가 우선 순위가 높은 프로세스 때문에 계속 기다리는 현상

What is a Wait Free algorithm?

Distributed Systems

What are the Fallacies of Distributed Computing?

  • The network is reliable.
  • Latency is zero.
  • Bandwidth is infinite.
  • The network is secure.
  • Topology doesn't change.
  • There is one administrator.
  • Transport cost is zero.
  • The network is homogeneous.
  • Fallacies of distributed computing

When would you use Request/Reply and when Publish/Subscribe?

  • 클라이언트에서 요청을 한 뒤 업데이트가 되야하면 Request/Reply
  • 요청이 없어도 업데이트가 되야하면 Pub/Sub

Suppose the system you are working on does not support transactionality. How would you implement it from scratch?

  • 임시 테이블과 트랜잭션 성공테이블을 나눈다

Software Lifecycle and Team Management

What is the biggest difference between Agile and Waterfall?

  • 고객과의 커뮤니케이션
  • 일정 주기를 가지고 점진적인 기능개발

How would you manage a very late project

  • 차를 만들지 않고 바퀴부터 만들겠다
  • 앱 환경에 따라 나중에 개발할 부분을 먼저 찾겠다

Are Program Managers useful?

  • 항상 필요하다
  • 클라이언트나 보스로 부터의 의견 절충을 해주지 않으면 설계도가 나와 건물 다 올렸는데 철근 추가하는 미친 짓을 해야되니까.

logic and algorithms

How would you sort a 10GB file? How would your approach change with a 10TB one?

  • 머지 소트

How would you programmatically detect file duplicates?

  • contents를 md5나 sha1 해싱 후 비교

Software Architecture

When is a cache not useful or even dangerous?

  • 서버로 데이터를 전송할 때

Why does Event-Driven Architecture improve scalability?

  • 노드 간의 이벤트가 있을때마다 로직을 처리할 수 있어서
  • 의존성을 줄일 수 있어서
  • 장애 복구가 쉬워서 (이벤트만 발급)

Scale out vs scale up: how are they different? When to apply one, when the other?

  • Scale out: 서버 대수를 늘리는 것, 수평 스케일, 분산처리
  • Scale Up: 서버 제원을 늘리는 것, 수직 스케일, 서버가 부담을 받을 때

What is CQRS (Command Query Responsibility Segregation)? How is it different from the oldest Command-Query Separation Principle?

What is Three-Tier architecture?

  • HTTP를 처리하는 Web Server

  • 웹 애플리케이션을 실행하는 WAS(Web Application Server)

  • 그리고 DBMS 로 각각의 계층으로 분리하는 3 Tier 방식의 아키텍처

  • 3 Tier 아키텍쳐

  • 다층구조

How would you design a software system for scalability?

   => Front        => Back
LB => Front => LB => Back => DB => Replication DB
=> Front => Back
=> Static Resources

What are the strategies to deal with the C10k problem?

  • C10k
  • 하나의 시스템 당 동접 수가 10k를 효율적 운영방안
  • 이벤트 드리븐과 비동기, LB로 부하 분산, 세션 수 제한 (기다리게)

How would you design a decentralized (that is, with no central server) P2P system?

  • BlockChain Node와 SockJS로의 P2P 데이터 동기화

What are the disadvantages of the Publish-Subscribe pattern at scale?

  • 디버깅
  • sub에서 메시지를 받았는지는 모른다

When is it OK to use tight coupling?

  • 강결합은 나쁜 것으로만 배워서 잘 모르겠다.
  • 이 답변은 너무 모호한 설명인 듯

A major advantage of a tightly coupled architecture is that it enables the rapid and efficient processing of large volumes of data, provides a single point of truth instead of several, often redundant, data sources, and enables open access to data throughout the organization.

Service Oriented Architecture and Microservices

What are the differences between Soa and Microservices?

Let's talk about web services versioning, version compatibility and breaking changes

시맨틱 버저닝

What are the pros and cons of MicroService architecture

  • 장점
    • 기능을 모듈로서 개발이 가능함
    • DB 또는 언어를 알맞게 선택이 가능함
    • 개별 디버깅, QA가 쉬움
  • 단점
    • 혼자 개발 못함
    • 엄청난 문서화가 필요함
    • 다른 서비스와 통신하는 비용 필요

Security

How do you write secure code? In your opinion, is it one of the developer's duties, or does it require a specialized role in the company? And why?

  • 린팅
  • 소나큐브
  • Gitlab 13.8 의 보안 체크
  • 방화벽

What do you know about Cross-Site Scripting?

What do you know about Cross-Site Forgery Attack?

  • CSRF는 인증된 사용자를 통해 공격자가 원하는 명령을 수행하게 하는 기법
  • CSRF 토큰을 사용해 페이지가 처음 로드 될 때 토큰 값을 가지고 있고, 서버에 요청을 보낼 때 토큰을 같이 보내 비교한다

How does HTTPS work?

What's a Man-in-the-middle Attack, and why does HTTPS help protect against it?

  • 전송 구간에서도 암호화가 되어 전송되니까 중간자공격이 통할리가.

How can you prevent the user's session from being stolen?

  • Session Expire를 당연히 줘야하고, IP 체크

General

What's the difference between TCP and HTTP

  • 4번째 레이어와 7번째 레이어
  • HTTP는 response 받으면 응답을 끊지만 TCP는 끊을 때까지 계속 연결
  • TCP가 하위니까 속도가 더 빠름

What are the tradeoffs of client-side rendering vs. server-side rendering

  • CSR은 CDN에
  • SSR은 Bot만이 들어올 수 있게.

12 팩터 앱

· 4 min read

개요

헤로쿠 사에서 클라우드-네이티브 아키텍처를 구축하면서 나온 패턴들이 12-팩터 앱 가이드로 정착. 단일 유닛으로 이뤄진 어플리케이션 아키텍처를 declarative configuration, statelessness, deployment independence에 초점을 두고 설계하는 안내서

MSA에 적용

12 factor

  • 코드베이스
  • 의존성
  • 설정
  • 백엔드 서비스
  • 빌드, 릴리즈, 실행
  • 프로세스
  • 포트 바인딩
  • 동시성
  • 폐기 가능 (쓰고 버리기)
  • 환경 일치
  • 로그
  • 관리 프로세스

코드베이스

  • codebase
  • 하나의 코드베이스는 VCS 에서 여러 배포버전을 기록하며, 하나의 앱은 하나의 코드베이스를 기반으로 소스 코드를 형상관리
  • 코드는 여러 대의 분산처리 운영서버나 여러 개발자의 컴퓨터로 배포, 실행될 수 있다

의존성

  • dependency
  • 의존성은 명시적으로 선언하되 격리한다
  • 인터넷이 차단된 개발 환경의 의존성을 어떻게 패키징할지도 고민 대상
  • 시스템 유틸리티에 의존하지 않는다

설정

  • configuration
  • 여러 서버의 접속 정보 등을 설정을 코드에 저장하지 않는다
  • 가변적인 것들은 환경변수를 이용한다

백엔드 서비스

  • backing service
  • 기반 서비스
  • 부가 리소스로 취급하며 URL 를 통해 서비스에 접속한다

빌드, 릴리즈, 실행

  • 빌드와 실행단계를 엄격히 구분한다
  • CI / CD 와 연관된다

프로세스

  • 단일/다수의 무상태 프로세스로 앱을 실행한다
  • 프로세스 사이에 아무 것도 공유하지 않고, 내부 상태를 갖고 있지 않는다
  • 모든 데이터는 백엔드 서비스로 다뤄야한다

포트 바인딩

  • 서비스는 포트를 바인딩하여 내보낸다
  • 한 앱이 다른 앱의 서비스가 될 수 있다

동시성

  • concurrency
  • 프로세스 모델 중심으로 scale out
  • 개별 프로세스는 멀티스레딩하면 된다
  • 같은 일하는 프로세스는 수직적, 다른일 하는 프로세스는 수평적

폐기 가능

  • disposability
  • 프로세스는 쓰고 바로 버릴 수 있어야한다
  • 프로세스가 갑자기 죽어도 서비스 장애가 발생해서는 안 된다

같은 환경

  • environment parity
  • 개발, 스테이징, 운영 환경을 최대한 비슷하게 유지한다

로그

  • 표준 콘솔창 (stdout)에 로그를 출력한다

관리 프로세스

  • admin process
  • 관리자가 수행하는 작업(DB migration, batch, at 등)은 일회성 프로세스로 실행한다
  • 데이터베이스를 직접 수정하지 않는다

참고

알고리즘 - 피보나치

· 4 min read

파이썬과 익숙해질 겸, 비지니스 로직 속에 빠져있는 나에게 기본을 잊지말자란 채찍질을 할 겸 뭔가를 시작해보았다

보통 피보나치는 재귀로 구현하는데, n 이 조금만 커져도 시간이 어마어마하게 증가한다 f(n-2) + f(n-1)의 반복이니 기존 값을 저장해뒀다가 쓰면 되어 배열로 처리한다는 걸 생각해 볼 수 있을 것이다

먼저 코드를 보자

해답

배열

fibonacci.py
def fibonacci(num):
fibos = [0, 1]

# 2 <= i < num + 1 까지 돌린다
for i in range(2, num + 1):
fibos.append(fibos[i-2] + fibos[i-1])

# 마지막 데이터가 구하려는 데이터가 된다
return fibos[num]

테스트해보면 재귀보다 엄청나게 빨라진 속도를 피부로 느낄 수 있다 뭐 여기까진 다들 생각해봤고 구현할 수 있을 것 같다

배열에 append하는 것 보다 미리 크기가 num만큼 정의된 배열에 i로 접근해 넣으면 더 빠른 속도가 날 것 같았다

하지만... python 엔 배열을 정해진 크기만큼 초기화할 방법이 없더라 [0] * num이라는 방법 뿐

배열을 안 쓰는 방법이 있을까? 한참을 고민하다가 다른 솔루션을 찾아봤다

재할당

멋진 방법이 있었다

fibonacci.py
def fibonacci(num):
a = 0
b = 1

# 0 <= i < num
for i in range(num):
temp = a
a = b
b += temp
return a

a 엔 b 값을 주고, b 에는 기존의 a 값을 계속 더해준다 이 짓을 num - 1만큼 반복한다

배열을 안 쓰니 절반정도의 시간이 더 줄어들었다

공식

이마저도 조금 아쉬움이 있었다, 피보나치는 수열이고 수열은 공식이 있기 마련이다 위키피디아엔 이미 그 공식이 있었다 수학의 힘을 빌려보자

fibonacci.py
from decimal import Decimal
## fibonacci의 85번 째부터 Double 값을 넘어가기에 Decimal 기능을 써야한다
sqrt5 = Decimal(5).sqrt()

def fibonacci(n):
return ((1 + sqrt5) ** n - (1 - sqrt5) ** n) / (2 ** n * sqrt5)

20 만번 째도 0.2 초만에 구해진다

여담

  • 피보나치의 데이터를 도식화하면 힙 모양이 되는데 아주 큰 값을 구하는 거라면 피보 힙이 제일 빠르지 않을까?
  • 수학의 정석 심화편이 생각나는 건 왜일까?

react에서 scrollTo 메소드로 스크롤 이동이 안될 때

· 2 min read

window.scrollTo(0, 0) 를 아무리 해봐도 스크롤이 맨 위로 올라가지 않을 때 많은 구글링을 한 뒤 다음과 같은 해결책을 적용해보았다

  • react-scroll 라이브러리를 써보고
  • 혹시 window 가 레이아웃 컴포넌트에 갇혀있어서 그런가 window 를 App.js 에서 Top 기능을 쓰는 곳까지 내려보고
  • jQuery 를 추가해서도 해보고
  • 별 짓을 다하다가

아무 것도 안 되서 리프레쉬하러 옥상에 갔다가 갑자기 든 생각이 있었다

잠깐만 component 라는게 하나의 element 안에서 쇼를 하는 거 잖아. 그럼 element 안에서 스크롤을 이동하면?

해결

Footer.js
class Footer extends Component {
scrollToTop = (event) => {
document.getElementById("root").scrollTo(0, 0);
};

render() {
return (
<div>
<div className="top">
<a onClick={this.scrollToTop}>위로가기</a>
</div>
</div>
);
}
}

index.js 에서 ReactDOM 을 렌더링해주는 element(나같은 경우엔 div#root)를 찾아서 그 엘레먼트의 스크롤을 올려주자

여담

책에서 봤는데 뇌가 쉴 때 가장 좋은 아이디어가 나온다고 했는데, 사실인 것 같다

windows에 spring boot cli 설치하기

· 3 min read

설치

스프링 부트를 설치해보자.

여기서 원하는 버전 스냅샷의 bin.zip 파일을 받아 압축을 풀어준다 예를 들면 spring-boot-cli-2.1.0.M1-bin.zipC:/spring에 압축을 풀면 된다.

정식버전은 여기의 zip 파일을 받으면 된다.

설정

환경변수를 3 개 추가해줘야한다

  • JAVA_HOME 변수에 JDK 설치 경로 (C:/Program Files/Java/jdk-10.0.2)
  • SPRING_HOME 변수에 방금 압축푼 spring cli 의 경로 (C:/spring)
  • path 변수에 %SPRING_HOME%\bin 경로

오류

git bash에서 org.springframework.boot.loader.JarLauncher 을(를) 찾거나 로드할 수 없습니다란 오류가 뜰 때는 spring/bin/spring파일을 열어 다음과 같이 수정하자

spring
## 16번째 줄 if 문 주석처리
## For Cygwin, ensure paths are in UNIX format before anything is touched.
## if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
## fi

## 94번째 줄 if 문 주석처리
## if $cygwin; then
PRING_HOME=`cygpath --path --mixed "$SPRING_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
## fi

출처

그래도 같은 오류가 발생한다면 spring 쉘 스크립트를 아예 새로 짜버리자

spring
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
## 아래에 다음 줄을 추가하자
## 이 구문은 스프링 홈 경로를 unix 스타일로 바꿔준다
[ -n "$SPRING_HOME" ] && SPRING_HOME=`cygpath --unix "$SPRING_HOME"`

## 94번째 줄을 다음과 같이 바꿔버리자
## if $cygwin; then
# SPRING_HOME=`cygpath --path --mixed "$SPRING_HOME"`
# CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
CLASSPATH=`cygpath --unix "$CLASSPATH"`
## fi

세 변수를 echo 찍었을 때 아래와 비슷하게 나오면 된다.

## echo $JAVA_HOME;
/d/Program Files/Java/jdk-10.0.2

## echo $SPRING_HOME;
/d/spring

## echo $CLASS_PATH;
.:/d/spring/bin:/d/spring/lib/spring-boot-cli-2.1.0.BUILD-SNAPSHOT.jar

그리고 마지막 줄의 명령어를 echo 할 때는 다음과 같다.

## echo "${JAVA_HOME}/bin/java" ${JAVA_OPTS} -cp "$CLASSPATH" org.springframework.boot.loader.JarLauncher "$@"
/d/Program Files/Java/jdk-10.0.2/bin/java -cp .:/d/spring/bin:/d/spring/lib/spring-boot-cli-2.1.0.BUILD-SNAPSHOT.jar org.springframework.boot.loader.JarLauncher

확인

$ spring --version

Spring CLI v2.1.0.M1

adonisjs에 response.sendStatus 추가하기

· One min read

express에 있는 sendStatus 기능을 활용하기 위해 start/hooks.js에도 다음 로직을 추가하자

소스

start/hooks.js
const { hooks } = require("@adonisjs/ignitor");

hooks.after.providersBooted(() => {
const Response = use("Adonis/Src/Response");

Response.macro("sendStatus", function (status) {
this.status(status).send("");
});
});

추가 후엔 controller에서 response.sendStauts(403)과 같은 응답을 반환할 수 있다

redux-form 사용하기

· 9 min read

react 로 form 을 만들어 사용하는 일은 생각보다 많은 걸 해야한다.

  • onSubmit에 이벤트를 잡아서 event.target.fieldName.value로 하나씩 체크를 해서 validating 을 하다가
  • 귀찮아질 때 쯤 function 을 만들어서 관리를 하고
  • 자주 사용하는 form input 을 컴포넌트로 만들며
  • form 에 초기값을 넣어줘야할 경우 (edit 페이지 같은) 값을 가져와 하나하나 바인딩하고
  • input 에 값이 바뀌었는지에 따라 submit 버튼을 활성, 비활성화 하는 로직도 있어야한다

번거로운 일을 하는 것 보다 redux-form 라이브러리를 사용해보자 검색시에 나오는 포스트들은 아주 기본적인 예제 밖에 없어서 문서를 매번 헤집는 시간이 필요했다.

먼저 이 라이브러리를 쓰기 위해선 다음 사전 지식이 필요하다

설치

yarn add redux-form

redux-form 의 리듀서를 연결시켜줘야한다

rootReducer
import { reducer as formReducer } from "redux-form";

const rootReducer = combineReducers({
// form 키를 사용해야한다
form: formReducer,
});

export default rootRecuder;

사용

redux-form component 와 redux-form 이 들어갈 compoent 를 만들어야한다 편의상 Login componentLoginForm component라고 하자

Login
import React, { Component } from "react";
import { connect } from "react-redux";
import LoginForm from "LoginForm";

class Login extends Component {
submitLoginForm = (formData) => {
console.log("LoginFormData => ", formData);
// this.props.loginAuth(formData)
};

render() {
return <LoginForm onSubmit={this.submitLoginForm} />;
}
}

export default connect()(Login);

LoginComponent에서는 딱히 특별한게 없이 LoginForm Component 를 호출하고 함수 하나를 내려준 게 끝이다

LoginForm
import React, { Component } from "react";
import { Field, reduxForm } from "redux-form";

class LoginForm extends Component {
render() {
const {
handleSubmit, // submit 이벤트를 redux-form에서 관리하게 수정
pristine, // form이 변경점이 없을 경우 true
submitting, // form이 submit 중일 경우 true
} = this.props;

return (
<form onSubmit={handleSubmit}>
<Field
name="id"
type="text"
component="input"
placeholder="아이디를 입력하세요"
/>
<Field
name="password"
type="password"
component="input"
placeholder="비밀번호를 입력하세요"
/>
</form>
);
}
}

export default reduxForm({
form: "LoginForm", // formReducer에 어떤 이름으로 이 폼을 등록할지
})(LoginForm);

redux-form에서 Field ComponentreduxForm High Order Component를 가져온 뒤 input tag 대신 Field Component 의 component 속성으로 input 을 만들어준다 그리고 해당 폼을 reduxForm 으로 감싸 redux 와 연결한다

handleSubmit은 onSubmit prop 으로 들어온 함수를 폼과 연결해준다 (event.preventDefault() 및 폼의 필드들을 serialize 해서 object 로 바꿔 onSubmit 함수의 parameter 로 넘겨준다)

말이 좀 어렵다면 Login.jssubmitLoginFormconsole.log(formData)의 구조를 보자

{
"id": "idValue",
"password": "passwordValue"
}

오 꽤 나이스하다 하지만 필드만 덩그라니 있는 경우는 없다 더 업그레이드 해보자

스타일이 필요한 필드

보통의 필드들은 label이 들어가 있고, selectBox, checkBox의 경우엔 스타일을 주기위해 대부분 외부 라이브러리를 쓴다 이럴 때 Field Component 의 component 속성에 원하는 모양을 만들어서 넣어주면 된다

LoginForm
...

const renderInputField = ({ input, type, label, placeholder, meta: { touched, error }}) => {
return (
<div>
<label>{label}</label>
<input {...input} type={type} placeholder={placeholder} />
{ touched && error &&
<span className="error">{error}</span>
}
</div>
)
}

class LoginForm extends Component {

render() {
const {
handleSubmit,
pristine,
submitting
} = this.props

return (
<form onSubmit={handleSubmit}>
<Field
name="id"
type="text"
label="아이디"
component={renderInputField}
placeholder="아이디를 입력하세요"
/>
<Field
name="password"
type="password"
label="비밀번호"
component={renderInputField}
placeholder="비밀번호를 입력하세요"
/>
</form>
)
}
}

renderInputField라는 함수형 컴포넌트를 만들어서 Field.component prop 에 넣긴했는데, div>input을 렌더링 해주는 것만 보이고 엄청난 파라미터들 때문에 정신이 아득할 것이다

component prop 에 함수형 컴포넌트를 넣으면 field prop 을 받게 된다 여기엔 내려준 prop 뿐 아니라 reduxForm 에서 넣어준 prop 도 존재한다 원래 해당 렌더링 컴포넌트는 아래와 같은 모습이다

const renderInputField = (field) => {
return <div>...</div>;
};

그럼 먼저 reduxForm 에서 넣어준 prop 을 보자

  • field.input: input 은 모든 HTML input attribute 를 가지고 있는게 아니라 다음 몇가지만 갖고 있다
    • checked
    • name
    • onBlur
    • onChange
    • onDragStart
    • onDrop
    • onFocus
    • value
  • field.meta: meta 는 해당 input 의 상태에 관한 데이터가 있다
    • touched: 해당 input 이 한 번이라도 클릭이 되어졌는지
    • valid: 해당 input 값이 valid 한지
    • error: 해당 input 값에 에러가 있으면 error message 가 들어온다
    • ... 나머진 문서를 참조하자

이제 type, label, placeholder 는 field.input prop 에 없어서 직접 넣어줬고, meta 는 이미 정의되어 있었구나 란 renderInputField 함수의 props 가 보일 것이고 Field Component 의 구조를 마음대로 변경할 수 있게 되었다

동적으로 value 를 바꿔야할 때

checkbox, select 의 경우엔 스타일이 들어가면 element 의 onClick 를 잡아서 hidden field 의 데이터를 바꿔줘야한다 checkbox 전체 영역을 클릭할 때마다 기존의 값을 toggle 해주는 component 를 만들 수 있다

LoginForm
const renderCheckboxField = ({ input, label }) => {
return (
<div onClick={(event) => input.onChange(!input.value)}>
<i
className={`fa ${!input.value ? "fa-square-o" : "fa-check-square-o"}`}
/>
<label>{label}</label>
<input type="hidden" {...input} />
</div>
);
};

value 에 따라서 font-awesome icon이 변경된다

함수로 값을 변경해야할 경우가 있을 수도 있다 이럴 땐 reduxForm prop 의 change 메소드를 사용하면 된다 바꿀 input 의 name 속성과 value 값을 넘겨주자

changeInputValue = (targetInputName, val) => {
this.props.change(targetInputName, val);
};

초기값

로그인 폼에는 아이디 저장 기능을 붙히면 폼이 초기화될 때 그 값을 가져와야한다

Login
...

class Login extends Component {
state = {
userId: ''
}

componentDidMount() {
const userId = localStorage.getItem('userId')

this.setState({
userId
})
}

render() {
return (
<LoginForm
onSubmit={this.submitLoginForm}
initialValues={{
id: this.state.userId
}}
/>
)
}
}

Login Component 에는 초기값을 가져와서 initialValues prop 안에 넣어준다

LoginForm
class LoginForm extends Component {
render() {
return <form>...</form>;
}
}

export default reduxForm({
form: "LoginForm",
// 이 값은 LoginForm 컴포넌트가 로드되고 나서
// initialValues가 바뀔 경우 폼 값도 업데이트가 되야되는지의 여부이다
enableReinitialize: true,
})(LoginForm);

localStorage.userId에 값이 들어있다면 그 값으로 id Field 가 초기화 된다 컴포넌트가 로딩되기 전에 값이 정해져있다면 enableReinitialize 옵션이 없어도 되지만, 수정 폼처럼 기존 데이터 fetch 후 또는 componentDidMount 후에 값을 가져온다면 해당 옵션값을 true로 넣어줘야한다

validation

여기까지 이해했다면 validation 은 큰 어려움 없이 문서의 예제소스를 참조하면 붙힐 수 있다

여담

react 라이브러리들은 읽은 뒤 바로 적용하긴 힘들다고 생각한다 매번 삽질이 필요한듯

react-intl로 번역 적용하기 (react i18n)

· 3 min read

리액트 앱에서 번역기능을 사용하려면 react-intl 패키지를 사용하면 된다. 근데 Documentation을 보면 format 기능들, HOC로 데이터를 넣는 등 여러 기능 덕에 정신이 아득하다

step by step으로 간단하게 적용해보자

설치

npm install react-intl

번역 데이터 생성

편의상 root에 locale.js로 만들었다. 서비스시엔 locale 폴더에 언어별로 파일을 나눠 관리하자.

locale.js
export default {
en: {
hello: "Hello",
},
ko: {
hello: "안녕하세요",
},
ja: {
hello: "こんにちは",
},
};

연동

index.js
import { IntlProvider, addLocaleData } from "react-intl";
// 이 서브 라이브러리들이 내 locale 파일을 사용할 수 있게 해준다
import en from "react-intl/locale-data/en";
import ko from "react-intl/locale-data/ko";
import ja from "react-intl/locale-data/ja";
import locale from "./locale";

addLocaleData([...en, ...ko, ...ja]);

// 저장되어 있는 언어 데이터를 가져온다
const defaultLang = localStorage.getItem("lang") || "en";

ReactDOM.render(
<Provider store={store}>
<IntlProvider locale={defaultLang} messages={locale[defaultLang]}>
<App />
</IntlProvider>
</Provider>,
document.getElementById("root"),
);

사용

import { FormattedMessage } from "react-intl";

<FormattedMessage id="hello" />;
// 저장되어 있는 언어 값에 따라 Hello, 안녕하세요, こんにちは 중 하나가 보여진다

inject

placeholder 등에서 텍스트만 필요할 때 component를 사용하지않고 다음과 같이 intl을 주입해서 사용한다.

import React, { Component } from "react";
import { injectIntl } from "react-intl";

class SignupForm extends Component {
render() {
const { intl } = this.props;

return (
<form>
<input
type="text"
name="id"
placeholder={intl.formatMessage({
id: "hello",
})}
/>
</form>
);
}
}

export default injectIntl(SignupForm);

child

HOC를 사용하지 않을 경우 FormattedMessage의 child로 번역된 문구를 받으면 된다

<FormattedMessage id="hello">
{(hello) => <input type="text" name="id" placeholder={hello} />}
</FormattedMessage>

dynamic

Hello, {Gracefullight} 처럼 동적으로 문구가 변해야할 경우valuesprop을 활용하자 먼저,locale.js에서 변수가 될 부분을 로 감싸준다

locale.js
export default {
en: {
helloUser: "Hello {user}",
},
ko: {
helloUser: "{user} 안녕하세요",
},
ja: {
helloUser: "{user} こんにちは",
},
};

values 아래 object로 변수 값을 넣어주면 된다

<FormattedMessage
id="helloUser"
values={{
user: "Gracefullight",
}}
/>

여담

mo, po 파일 건들던 시간들이 너무 아깝다