※ 이 글은 How to Use Extend with TypeSript 를 번역한 글입니다.

 

출처 - 구글 이미지 검색

mkdir src && cd src
mkdir __tests__
mkdir typings
mkdir example

 

Jest 는 현재 가장 인기 있는 테스팅 라이브러리 중 하나 입니다. 코드의 안정성과 신뢰성을 보장해주기 위해서 테스트 코드의 작성 여부는 선택이 아닌 필수가 되어버렸습니다. 

 

 

 

최근 1년간 NPM 다운로드 현황 (출처 - NPM 트렌드 현황)

 

 

기본적으로 Jest 는 여러 환경에서 테스트 코드를 작성할 수 있는 환경을 제공합니다. 물론 이 환경에서는 비동기 호출이나 다소 복잡했던 로직들도 테스트할 수 있는 환경 역시 구축할 수 있습니다. 그런데 Jest 가 제공하는 환경에서가 아니라 좀 더 구체적으로 내가 작업하고 있는 환경에 맞게 테스트하고 싶은 경우는 어떻게 해야할까요. 저의 경우는 "서버로 데이터를 요청하는 API 요청에 대한 응답값이 JSON 스키마와 항상 일치하는지 어떻게 확신할 수 있을까?" 에 대한 고민이 있었습니다. 왜냐하면 실제 프로덕션 레벨에서 다뤄지는 서버 데이터는 매 요청마다 결과 값이 다를 수 있기 때문에 상황이 복잡합니다. 주로 실시간 데이터인 항공 데이터나 숙박 검색과 같은 경우가 이런 경우입니다. 

 

 

이럴 때 Jestextend 는 커스터마이징 테스트 메소드를 구현할 수 있게 해줍니다.

 

 

 

 

프로젝트 구성하기

작업을 하기에 앞서 먼저 프로젝트를 구성을 시작하겠습니다.

npm init -y

 

 

필요한 패키지 역시 설치해 줍니다.

npm i -D jest ts-jest typescript @types/jest

// package.json
"scripts": {
  "test": "jest"
}

 

 

 

마지막으로, 폴더를 생성하면 끝입니다.

mkdir src && cd src
mkdir __tests__
mkdir typings
mkdir example

 

여기까지 따라왔다면, 디렉토리 구조는 아래와 같이 생성 됐을 것입니다. 간단해 보이지만 (실제로 간단하지만) 여기까지 잘 따라오셨다면 이제 모든 준비가 된 겁니다!

node_modules
src
|--- __tests__
|--- example
|--- typings
package-lock.json
package.json

 

 

 

 

 

설정 파일 작성하기

이 단계에서는, 타입스크립트가 우리가 테스트 코드를 작성할 때, 새롭게 만든 타입 함수들을 사용할 때 그게 뭔지 이해할 수 있도록 하는 사전 작업이 필요합니다. 별 다른 어려움이 없으니 천천히 따라해보세요.

 

 

먼저, 루트폴더에 jest.config.js 파일과 tsconfig.json 파일을 만드시기 바랍니다. 이 파일들은 테스트 코드를 실행할 때 파일 런타임 때 자동으로 읽히게 될 것입니다. 설정 파일을 따로 만들고 싶지 않다면 package.json 파일에 설정을 추가하는 식으로 대신할 수도 있습니다. 좀 더 많은 정보가 필요하다면 Jest 환경 설정을 클릭하세요.

 

touch jest.config.js
cd src && touch tsconfig.json

 

 

 

 

 

 

 

 

 

 

 

 

 

기본 테스트코드 작성

이제 기본적인 테스트 코드를 작성해 봅시다.

 

cd src && cd example
touch math.ts

 

 

 

 

 

그리고 방금 생성한 math.ts 파일을 테스트할 테스트 파일을 만듭니다.

테스트 파일을 만들 때 주의할 점은 반드시 테스트 파일 이름은 *.spec.* 형식으로 지어져야 합니다.

 

cd __tests__
touch math.spec.ts

 

 

 

 

 

mockedAdd 는 spy 함수로 spy 함수내에 파라미터로 전달된 원래 함수(math.add)가 사용됐을 때 추적을 할 수 있도록 하는 아주 유용한 기능의 메소드입니다. 원래 함수가 만약 객체라면 spy 함수는 그 객체의 주소 값을 복사한 값을 갖고 있는 새 변수라고 생각하는 것이 더 쉬울 수도 있겠습니다.

 

const initialObj = { sayHi: () => console.log('hi') };

// initialObj 의 주소 값을 가집니다.
// initialObj 의 변경점에 대해서 실시간으로 같이 공유받을 수 있다는 점을 기억하세요.
const spyObj = initialObj;

// spyObj 와 비슷한 개념으로(그러나 엄연히 다른 개념입니다) 기존 객체의 메소드의 변경 사항을 
// 계속 실시간으로 추적할 수 있다고 생각해보세요.
// 어떤 파라미터로 불렸는지, 몇 번 실행 됐는지 등 다양하게 사용처를 추적할 수 있습니다.
const spyFn = jest.spyOn(initialObj, 'sayHi');

spy 함수에 대해 더 자세한 정보를 보고 싶으시면 Jest 공식 문서를 참조하세요.

 

 

테스트 코드를 보면 expect() 를 통해 테스트하는 부분에서 서로 다른 방식으로 테스트를 하고 있는 부분을 보실 수 있을 겁니다. 위에서 짧게나마 소개한 spy 함수와 원래 함수의 반환 값을 이용해 테스트를 진행할 수도 있고 mock 객체에 포함된 calls, results 같은 원본 함수 추적용 배열을 사용해 테스트를 할 수도 있습니다.

 

 

그런데 테스트 코드를 작성하다보면 기본 테스트 메소드를 사용할 수 없는 경우가 있습니다. 예를 들어 값을 전달 받아 분기 처리를 한 후에 결정해야하는 경우 등이 그렇습니다. 그럴 때마다 그럼 테스트 코드를 이런식으로 작성해야 할까요?

 

if (num % 2 === 1) {
  expect(num).toBe(yourOdd);
} else {
  expect(num).not.toBe(yourOdd);
}

 

 

expect() 를 사용해 저런식으로 가정법과 같이 값에 따라 결과가 불확실한 형태로 테스트 코드를 작성하는 건 권장하고 싶지 않습니다. 테스트 코드 블록의 느낌을 산만하게 만들고 한 눈에 들어오기 점점 더 어려워지기 때문입니다.

 

 

이럴 때 사용하는 기능이 있습니다.

 

 

expect.extend()

Jest 는 테스터들이 좀 더 자유롭게 테스트 함수(이제부터는 matchers 라고 부르겠습니다) 를 입맛에 맞게 작성해서 사용할 수 있는 자율성을 갖고 있습니다. 타입스크립트에서 Jest 를 사용하는 경우, 타이핑 파일에 커스텀 matchers 에 대한 타이핑 정보가 없기 때문에 우리는 타입스크립트가 이해할 수 있도록 타이핑 파일을 먼저 작성하는 것부터 시작하겠습니다.

 

 

expect() 로 넘기는 값이 홀수인지 판별하는 커스텀 matcher 를 만들겠습니다.

 

 

cd src && cd typings
touch index.d.ts

 

 

 

 

 

 

여기까지 따라왔을 경우 폴더 구조는 다음과 같아야합니다.

 

node_modules
src
|--- __tests__
|    |
|    |--- math.spec.ts
|--- example
|    |
|    |--- math.ts
|--- typings
|    |
|    |--- index.d.ts
|
|--- tsconfig.json
jest.config.js
package-lock.json
package.json

 

typings 폴더를 만들고 그 안의 타이핑 파일에 toBeOdd() 에 대한 타입을 정의해주었지만 아직은 테스트 코드에 그 어떤 영향도 미치고 있지 않습니다. 타이핑 파일은 단순히 타입스크립트가 toBeOdd() 라는 메소드가 존재하고 어떤 파라미터를 받고 어떤 형태의 값을 반환해야하는지 알 수 있게해주는 파일입니다. 더 쉽게 말하자면 예를 들어서 window. 같이 window 를 입력하고 . 를 입력했을 때 내부에 포함된 메소드 목록들 중 하나로 나올 수 있게 타입스크립트한테 정보를 전달해주는 파일입니다. 

 

 

이제 테스트 코드로 돌아가서 실제 사용법을 보겠습니다.

 

 

 

 

 

 

 

 

 

 

 

우리가 만든 새로운 matchertoBeOddreceived 라는 파라미터를 제외하고 아무 파라미터도 받지 않는 함수입니다. received 는 테스트 코드에서 expect() 를 호출했을 때 그 안에 넘겨주는 값입니다. 즉 expect(10) 으로 시작했다면 received 의 값은 10 입니다. 위 코드에서는 received 는 각각 2 와 3 입니다. 

 

 

toBeOdd 메소드에 파라미터를 추가로 넘기고 싶다면 타이핑 파일부터 다시 수정해야하는데, 관련된 변경 사항들만 짚어보자면 다음과 같이 될 겁니다.

 

// index.d.ts
declare global {
  ...
  interface matchers<R> { 
    toBeOdd(a: number): R;
  }
}
// math.spec.ts
expect.extend({
  toBeOdd(received, a) {
    .. do something
  }
});
it('..', () => {
  /* received will be 3, and a will be 2 */
  expect(3).toBeOdd(2);
})

 

 

matchermessagepass 라는 값을 포함한 객체를 반드시 반환 값으로 반환해야 합니다.

 

 

message - 테스트가 실패 했을 경우 콘솔에 보여줄 오류 메시지를 반환하는 메소드입니다. 아무 인자도 받지 않습니다.

pass - boolean 타입의 값으로 어떤 형식으로 테스트가 불렸는지에 따라 각각의 message 함수를 리턴할 수 있게 해줍니다.

 

 

예를 들어서 pass 가 true 인 경우, message 함수는 matcherexpect(value).not.toBeOdd() 같은 형식으로 불려진 상황에서 테스트가 실패했을 때 실행됩니다. 반대로 pass 가 false 인 경우는 matcherexpect(value).toBeOdd() 의 형식으로 테스트가 실행된 상태에서 테스트가 실패했을 때 실행되게 됩니다.

 

 

/*
* pass: false
* 2 는 홀수가 아니므로 테스트는 실패합니다
* message 함수가 반환하는 메시지는 
* 'expected 2 not to be an odd number'
*/
expect(2).toBeOdd();
/*
* pass: true
* 1 은 홀수이므로 테스트는 실패합니다
* message 함수가 반환하는 메시지는 
* 'expected 1 to be an odd number'
*/
expect(1).not.toBeOdd();

 

 

 

최종 코드

 

npm run test math

 

 

 

 

 

결론

자바스크립트 테스팅 라이브러리는 프로젝트가 배포되기 전 코드의 품질을 보증할 수 있게 해줍니다. 그래도 테스트 코드를 작성하다보면 어느 테스팅 라이브러리를 사용하고 있든 상황에 맞는 필요한 matcher 를 찾기 힘든 상황이 오기 마련입니다. 그래서 이번 포스팅에서 소개해드린 기능은 여러분이 작성하고 있는 테스트 코드의 유연함을 더욱 증가시켜줄 것입니다.

※ 알림

이 글은 원작자의 허락을 받고 번역한 번역글입니다.

번역의 부드러운 이해를 위해 생략, 의역 및 추가한 부분이 있을 수 있습니다.

 

원본 글 보러가기

 


 

우리가 매일 사용하는 이 아름다운 도구가 실제로는 어떻게 동작하고 있는지 생각해본 적이 있나요? 컴파일러는 실제로 어떻게 돌아가고 있을까요? 입맛에 맞게 컴파일 옵션을 바꾸고 싶진 않으시구요? 이 포스팅이 아마도 제법 도움이 될 거라고 생각합니다.

 

 

바벨(Babel)은 실로 굉장히 멋진 녀석입니다. 페이스북, 구글, 넷플릭스 외에도 수 백개의 다른 기업들이 이용하고 있지요. npm 월 다운로드 수는 이미 7백만을 넘어선지 오래입니다. 바벨은 ES6 뿐만 아니라 ES7, JSX, 폴리필(Polyfill), 플로우(flow) 까지 지원하고 있습니다. 만약 이 것들이 충분하지 않다고 느끼신다면 TC39(JavaScript 를 관리하고 리드하는 기술 위원회)에서 관리하고 있는 아직 정식 버젼에 포함되지도 않은 그야말로 최신 기능까지도 사용할 수 있게 해줄정도로 바벨은 굉장히 멋진 녀석입니다.

 

 

자, 이제 사설은 이쯤에서 마무리짓고, 본론으로 들어가봅시다. 

바벨은 소스 컴파일러입니다. 조금 더 자세하게 말하자면, 아래의 예를 보는게 더 나을 것 같습니다. 

ES6의 애로우 펑션(Arrow Function)입니다.

 

만약, JavaScript에서 이 함수를 사용할 일이 있다고 해보겠습니다.

(foo, bar) => foo + bar;

 

브라우저는 위 코드가 아래와 같은 형식이길 기대합니다.

"use strict"

(function(foo, bar) {
	return foo + bar;
});

 

 

위의 과정은 3 단계로 나뉘어져 있습니다.

1 단계: 파싱(Parsing)

바벨은 먼저 소스 코드를 가지고 추상적인 형태의 코드로 변환하는 과정을 수행하는데, 이를 바벨에선 파싱(Parsing)이라 표현합니다. 그리고 이 추상적인 형태의 코드는 추상구문트리(AST: Abstract Systax Tree) 라고 부릅니다.

 

 

추상구문트리(AST)는 추상화된 코드의 표현체입니다. 각각의 노드는 소스코드의 구조를 의미합니다. 이 추상구문트리는 소스코드의 좀 더 원활한 변환(transformation)을 위해 작성되는 표현체입니다.

 

 

바벨의 수 많은 플러그인 중에선 babel-parser 라고 불리는 플러그인이 있는데, 이 babel-parser 의 또 다른 이름은 바빌론(bablyon) 입니다. 추상구문트리는 바빌론에 의해 작성됩니다. 추상구문트리는 위의 애로우 펑션(Arrow Function)을 다음과 같이 표기합니다.

// AST shortened for clarity
{
    "program": {
        "body": [
            {
                "type": "ExpressionStatement",
                "expression": {
                    "type": "ArrowFunctionExpression",
                    "params": [
                        {
                            "type": "Identifier",
                            "name": "foo"
                        },
                        {
                            "type": "Identifier",
                            "name": "bar"
                        }
                    ],
                    "body": {
                        "type": "BinaryExpression",
                        "left": {
                            "type": "Identifier",
                            "name": "foo"
                        },
                        "operator": "+",
                        "right": {
                            "type": "Identifier",
                            "name": "bar"
                        }
                    }
                }
            }
        ]
    }
}

 

추상구문트리에서 볼 수 있듯이, 이 트리는 소스코드의 각 구문과 각 코드간의 관계를 모두 서술하고 있습니다. 추상구문트리는 아래의 사이트에서 좀 더 이리저리 자유롭게 바꿔가며 실험해보실 수 있습니다.

 

>[추상구문트리 변환 사이트 바로가기]

 

 


[재미로 보는 내용]

바벨(Babel)과 바빌론(Bablyon)은 모두 구약성경의 내용과 깊은 연관이 있습니다. 사실 사전에는, 바벨과 바빌론은 모두 고대 바벨로니아의 도시를 뜻하는 용어라고 표시되어 있습니다. 단지 영어로 표기하면 Bablyon, 히브리어로 표기한다면 Bável 으로 표기됩니다. 사실 우리는 바벨과 바빌론보다는 바벨탑을 조금 더 친숙하게 느끼고 있습니다. 바벨탑은 신에게 닿기 위해 높이 쌓아올린 탑이라고 알려져 있죠. 

 

창세기 11장 4절에는 이런 구문이 있습니다.

또 사람들은 논의하였다. "어서 도시를 세우고 그 가운데 꼭대기가 하늘에 닿게 탑을 쌓아 우리 이름을 날려 사방으로 흩어지지 않도록 하자."

... (중략)

주님께서 온 세상의 말을 거기에서 뒤섞어놓아 사람들을 온 땅에 흩으셨다고 해서 그 도시의 이름을 바벨이라고 불렀다.

 

JavaScript 의 브라우저들이 저마다 각각 다른 호환성을 보장하는 것이 싫어 탑을 높게 쌓아올려(BabelJS) 호환성을 하나로 묶고 싶었던 마음에서였을까요? BabelJS 의 플러그인들에서 보여지는 네이밍들은 구약성경의 바벨과 어딘가 모르게 연관되는 듯한 느낌마저 주는듯 합니다.


 

2단계: 변환(Transformation)

이 단계가 바로 마술이 펼쳐지는 곳입니다. 바벨은 1단계에서 파싱된 추상구문트리를 받아와 각 브라우저의 환경에 맞는 결과로 변환하는 작업을 수행합니다.

 

 

바벨의 플러그인 중 preset/plugin 에 의해 처리되는 곳이기도 합니다. preset 은 특별한 건 없고 plugin 들을 모아놓은 배열입니다. 단지 plugin 을 전부 하나씩 수동으로 등록할 수고를 덜어줄 뿐입니다. 바벨에서 지원하고 있는 preset/plugin 을 사용해도 좋고, 따로 입맛에 맞게 새로 만들어서 사용해도 상관없습니다.

 

 

이 플러그인들은 babel-traverse 를 이용해 원본 추상구문트리를 가로질러가는(traverse) 과정 속에서 각 부분들이 어떻게 바뀌고 어떻게 정의되어야하는지를 기록합니다.

 

 

1단계에서 생성된 애로우 펑션(Arrow Function) 에 대한 추상구문트리는 2단계에 접어들게되면서 babel-traverse 에 의해 재작성됩니다. 이 과정에서 바로 위에서 설명한 것처럼 브라우저 호환 환경에 맞게 적절한 형태로 변환될 수 있도록 안내 가이드같은 것들이 적히는 변형(transformation) 과정을 거치게되고, 이렇게 일련의 변형을 마치면 새로운 추상구문트리로 바뀌게 됩니다. (이 과정은 바벨의 플러그인 코드가 관여합니다)

 

아래는 새롭게 탄생한 추상구문트리입니다. 비슷해보이겠지만, 1단계에서 만들어진 추상구문트리와는 다른 점이 있습니다.

// AST shortened for clarity
{
    "program": {
        "type": "Program",
        "body": [
            {
                "type": "ExpressionStatement",
                "expression": {
                    "type": "Literal",
                    "value": "use strict"
                }
            },
            {
                "type": "ExpressionStatement",
                "expression": {
                    "type": "FunctionExpression",
                    "async": false,
                    "params": [
                        {
                            "type": "Identifier",
                            "name": "foo"
                        },
                        {
                            "type": "Identifier",
                            "name": "bar"
                        }
                    ],
                    "body": {
                        "type": "BlockStatement",
                        "body": [
                            {
                                "type": "ReturnStatement",
                                "argument": {
                                    "type": "BinaryExpression",
                                    "left": {
                                        "type": "Identifier",
                                        "name": "foo"
                                    },
                                    "operator": "+",
                                    "right": {
                                        "type": "Identifier",
                                        "name": "bar"
                                    }
                                }
                            }
                        ]
                    },
                    "parenthesizedExpression": true
                }
            }
        ]
    }
}

 

애로우 펑션(Arrow Function) 플러그인 코드에 대해 좀 더 살펴보실 분들은 링크를 클릭하시기 바랍니다. 그리고 바벨 플러그인 핸드북 가이드 역시 추상구문트리에 대해 좀 더 자세히 이해할 수 있도록 도와줄 것입니다.

 

 

3단계: 코드 생성(Generation)

3단계에서는 2단계에서 생성된 새로운 추상구문트리를 바탕으로 실제 브라우저 환경에 맞는 소스코드로 변환하는 과정이 이뤄집니다. 2단계에서 만들어지는 추상구문트리는 각 브라우저에서 코드 구문이 어떻게 바뀌어야하는지에 대한 내용을 기술한다고 설명했습니다. 이렇게 기술만하는 단계가 2단계라면, 실제로 이 내용을 바탕으로 코드를 생성하는 곳이 3단계입니다.

 

"use strict";
(function (foo, bar) {
  return foo + bar;
});

 

이 과정은 babel-generator 에 의해 이뤄집니다.

 

 

결국, 바벨의 동작 과정에 대한 전체 개요는 아래와 같이 정리할 수 있습니다.

 

 

 

결론

위에서 설명한 이 모든 과정이 바벨이 실제로 우리가 모르게 동작하고 있었던 과정입니다. 이 글이 모든 것을 설명하고 있지는 않습니다. 더 자세한 내용을 보고 싶다면 이 문서를 참고해보는 것도 도움이 될 겁니다.

 

 

번역 한 줄 후기

최근 바벨에 대한 기초가 너무 부족하다고 느꼈습니다. 웹팩으로 환경설정을 할 때도 뭐를 설치해야하고 버젼마다 뭐가 또 다르고.. 너무 복잡하고 끝도 없는 느낌이라 감도 안왔었습니다. 그래서 공식문서도 읽을 수 있을만큼 읽어보려고하고 동영상들도 많이 찾아보려고 했지만 전부 사용 방법에 대해서만 알려줄 뿐이었습니다.

 

 

그래서 이 글은 너무나도 저한테 한 줄기 빛과 같았습니다. 물론 전체를 자세히 다루진 않았다고 확신합니다. 그 것은 이제 각자의 몫이겠죠. 그러나 적어도 숲에 대한 개요를 요약해놓은 글이 있다는 것만으로도 충분히 번역할만한 가치가 있다고 생각했고, 시간을 들여서 기록으로 남기게 되었습니다.

 

 

좋은 글을 남겨준 원작자에게 감사를 표합니다.

 

2019/03/31 - [Web Tech/React] - React hooks (리액트 훅) 사용 방법 시리즈(1) - useState

2019/03/14 - [Web Tech/Git] - Git을 사용할 때 꼭 알아두어야 할 개념들

2019/03/31 - [Web Tech/JavaScript] - JavaScript(jQuery) - position sticky IE polyfill(폴리필) 방법

단편 시리즈처럼 React hooks API 의 사용법에 대해서 기록해볼 예정입니다.

React Hooks 의 자세한 개요는 공식 홈페이지에서 확인할 수 있습니다.

useState

state 를 편하게 관리할 수 있는 가장 기본적인 API 입니다.
아마 useState 는 React hooks API 중에서 가장 많이 사용되는 혹은 가장 많이 사용될 API 가 아닐까 싶네요.

기존엔 state 를 사용하려면 다음과 같이 코드를 작성해야 했습니다.

 

 

클래스 기반의 컴포넌트를 작성하고 그 안에서 state 를 선언하고 관리하는 방식이었습니다.

이러한 방식의 단점은 관리할 state 가 많아지면 코드의 가독성이 떨어지는 문제가 있었습니다.

그리고 redux, mobx 와 같은 스토어 중심의 상태 관리 시스템이 등장하면서 state 를 사용하는 일이 크게 줄어들었습니다.

그래서 React hooks API 가 등장하기 전까지는 state 는 이제 레거시와 같은 느낌의 날 것으로 남아있는 그런 느낌도 들었으니까요.

React hooks 가 등장한 좀 더 구체적이고 명확한 이유는 공식 홈페이지에서 업로드한 영상에서 확인하실 수 있습니다.

어쨌든, useState 를 사용하면 어떤 코드가 되는지부터 살펴보겠습니다.

 

 

보기에는 크게 다를게 없어보이지만 다음과 같은 차이점이 존재합니다.

 

클래스 기반에서 함수형 기반으로

React hooks API 는 클래스형 컴포넌트에서 사용할 수 없습니다.
이런 배경이 되는 이유는 왜 React hooks 가 등장했는지를 알아야하는데요.
공식 홈페이지의 리액트 소개글에는 이런 문구가 있습니다.

 

We’ve found that classes can be a large barrier to learning React.
...
The distinction between function and class components in React and when to use each one leads to disagreements even between experienced React developers.

저희 개발팀은 클래스가 리액트를 배우는데에 큰 장벽이 될 수 있다는 점을 느꼈습니다.
...
React 에서 함수형과 클래스형 컴포넌트를 두고 언제 어떤걸 사용해야하는지에 대한 의견은 리액트를 오랫동안 사용한 전문가들 사이에서도 의견이 분분합니다.

 

위 글만 보더라도 React 측에서도 함수형 컴포넌트를 그다지 선호하지 않는다는 점을 미뤄 짐작할 수 있습니다.
개인적으로는 자바스크립트에서 초심자에게 가장 이해하기 어려운 난해한 개념들이 바로 프로토타입과 this, 클로져 라고 생각하는데 React 측에서도 이러한 점을 어느정도 수긍하고 있고, 리액트를 사용하는 개발자들이 반드시 자바스크립트에 익숙한 개발자라는 보장이 없다는 점을 전제로 한 패치방향이라고 볼 수 있을 듯 합니다.

state 업데이트 방식

기존의 클래스 컴포넌트 방식에선 state 를 업데이트하기 위해선 setState 메소드를 사용해 state 를 업데이트 할 수 있었습니다.
새롭게 도입된 React hooks 에선 useState 메소드를 사용할 수 있습니다.
사용 규칙은 아주 간단합니다.

 

예를 들어서 state 로 사용하고 싶은 변수명이 count 라면, setState 역할을 할 메소드는 state 변수명 앞에 set 을 붙이고 카멜케이스 기법에 따라 네이밍을 처리하면 됩니다. 즉, setCount 가 되겠죠.
그리고 count, setCount 는 대괄호로 감싸줍니다.
useState 에 전달할 파라미터 값으로는 state 변수를 초기화하고 싶은 값을 넣으면 됩니다.
딱히 초기화하고 싶지 않다면 null 을 넣어주어도 무난합니다.

앞서 설명한 방식을 적용한다면, 이런 식으로 사용할 수 있을 겁니다.
const [count, setCount] = useState(0);
초기화 할 값은 0 으로 지정해주었고, 네이밍은 카멜케이스에 맞게 정해주었습니다.

state 값을 업데이트할 땐 아주 간단합니다.
setCount 메소드를 호출하고 그 안에 값을 넣기만하면 끝입니다.
자동으로 count 가 업데이트가 될 것이고, this 를 사용할 필요도, this bounding 에 대한 고민 또한 전혀 필요 없습니다.
왜냐면 함수형 컴포넌트이기 때문이죠!

 

컴포넌트 지옥 탈출

React 로 개발을 하다보면, 이러한 고민에 종종 빠집니다.
"이 컴포넌트 보기가 좀 싫은데.. 분리시켜야하나?"

예시를 한 번 들어보겠습니다.

 

 

컴포넌트를 뭔가 조금 더 깔끔하게 하고 싶었는데, componentDidMount 등과 같은 생명주기 메소드를 사용해야했기 때문에 클래스형 컴포넌트 사용이 강제될 수 밖에 없었고 결국 컴포넌트를 하나 더 만들어서 함수형 컴포넌트로 작성했습니다.
아마 React 를 사용하시는 대부분의 분들이 이런 비슷한 경험을 했거나 고민을 한 적이 있을거라고 생각합니다.
실제로 운영중인 프로젝트에선 그 깊이가 상당히 깊을 수도 있겠네요.

React hooks API 는 기본적으로 이러한 고민을 없애고자(줄이고자) 함수형 컴포넌트 기반에서 컴파일되도록 설계된 메소드들입니다.
그래서 함수형 컴포넌트 기반으로 깔끔하게 작성할 수 있습니다.

 

예제 코드

예제 코드를 아래에 첨부하겠습니다.

 

두 개의 다른 버전에서 동작하는 모습을 확인해보세요.
이렇게 React hooks 에 추가된 메소드 중 하나인 useState 에 대해서 알아봤습니다.
다음엔 다른 메소드를 기록해볼 예정입니다.

 

참고가 되었으면 좋겠습니다 :D

새로운 Position Sticky

position: sticky 는 정말 유용한 기능으로 고정 픽스 네비게이션 등을 구현할 때 아주 편리합니다.
다음과 같은 코드에 다음과 같이 적용만해주면 바로 적용이 되는데요.

다음과 같은 코드는 스크롤을 내릴 때 nav 엘리먼트가 상단에 고정되어 따라다니게 해줍니다.
참 좋은 기능이죠. 그러나 아주 치명적인 단점이 있습니다.

IE 에서 지원하지 않는 기능입니다.

 

브라우저 지원 현황

이게 얼마나 치명적인 느낌인지 모르시는 분들을 위해..

2018년 한국의 브라우저 사용 점유율

위 사진은 2018년 한 해를 기준으로 한국에서 데스크톱의 경우 각 브라우저의 점유율을 나타내주는 지표입니다.
위 지표를 보러가시려면 이 곳을 클릭하세요.
IE는 점유율이 지속적으로 낮아지고 있지만 아직까지도 전체 사용량의 약 20%를 차지할만큼의 비율을 보여주고 있습니다.
다시 말하자면, 어떤 서비스를 런칭할 때 20%의 비율은 절대 무시할 수 없는 큰 비율입니다.
따라서 반드시 고려해야하는 상황이죠.

position: sticky 의 브라우저 지원 현황

브라우저 지원 현황은 무난한 편입니다. 대신 위에서 언급한 것 처럼 IE 가 전혀 지원을 하지 않는 상황이네요.

Polyfill 구현

그렇다면 이 문제를 해결하는 방법은 아예 없는 걸까요? 그렇지는 않습니다.

 

1) 기존의 스펙 변경

이게 무슨 해결책이냐 할 수도 있지만 어떤면에선 가장 빠르고 확실한 해결책입니다.
문제가 되는건 IE 뿐인데, 이 브라우저만 제외하면 모든 브라우저에선 동작하죠.
position: sticky 는 지원하는 브라우저에서는 동작하지만 지원하지 않는 브라우저에서는 동작하지 않습니다.
만약에 현재 운영중인 어플리케이션을 이용하는 사용자의 IE 비율이 많이 낮거나 무시할정도가 된다면, 재협의를 통해 다시 스펙을 변경하는 것도 좋은 방법이라고 생각합니다.

 

2) StickyBits 라이브러리 사용하기

position: sticky 를 구현할 수 있도록 도와주는 라이브러리가 존재합니다. StickyBits 라고 부르는 라이브러리인데, 제법 유명한 라이브러리인 것 같습니다.
라이브러리 답게 여러가지 추가 기능이 붙어있으며 이슈 제기에도 제법 빠르게 답변해주는 편입니다.
그러나 저는 이 라이브러리를 사용하지 않았습니다.

 

3) 직접 구현하기

2번을 사용을 하지 않았던 이유는 사실 제가 사용했을 땐 IE 에서 여전히 동작하지 않았습니다.
왜 그런지는 코드 내부를 들여다보지 않아서 모르겠지만요.
해당 깃허브에 PR 을 보내 코드 수정을 요청하는 것도 하나의 방법입니다만,
코드 안정성 검사를 받아야하기 때문에 시간이 걸리죠.

사실 귀찮습니다.

우선 마크업이 다음과 같이 있다고 가정합시다.

여기에 적용할 코드는 다음과 같습니다. position: fixed; top: 0;
자바스크립트를 사용해 현재 스크롤의 위치에 따라 네비게이션을 고정하는 방식입니다.

네비게이션의 top 값을 가지고와서 window.pageYOffset 과 비교하면서 position: fixed 의 여부를 결정하는 로직이 메소드 안에 들어있습니다. 그런데 이런 방식은 문제점이 있습니다.

 

Position: fixed 방식의 문제점

이러한 방식은 문제점이 있습니다.
아래의 코드를 첨부하겠습니다.

See the Pen QPLzWy by Munkyu Yang (@moonformeli) on CodePen.

스크롤을 내리다보면 뭔가 이상합니다. 네비게이션이 상단에 고정이되서 붙어 다니긴하는데, 고정이 될 때
다른 글이 팍! 하고 위로 올라가는게 느껴지시나요?

position: fixed 로 요소가 더 이상 그 공간에 종속되지 않으면서 생기는 사이드 이펙트입니다.
그럼 네비게이션 부분만큼이 빠지니까 당연히 그 아래의 컨텐츠들이 위로 올라가는거죠.

이 부분을 고정해줄 무언가가 필요합니다.

사이드 이펙트 해결

position: fixed 로 빠진 부분을 어떻게 채울 수 있을까요.
해결 방법은 똑같은 요소를 만들어서 대신 넣는다 입니다.

cloneNode 의 옵션값을 true 로 넣어주면 자식 요소들까지 복제가 됩니다. cloneNode API 보러가기
자식들까지 복제하는 이유는 자식 요소가 없으면 요소를 복제한다해도 height 값이 0 이기 때문입니다.

네비게이션을 고정할 때 복제한 요소를 넣어주고, 고정을 해제할 때 복제한 요소를 제거하는 방식으로하면 IE 에서도 부드럽게 구현됩니다.

구현 결과는 다음과 같습니다.

See the Pen rbBodd by Munkyu Yang (@moonformeli) on CodePen.

부드럽게 동작하네요!

codepen 은 IE 에서 동작하지 않습니다. 테스트를 원하신다면 아래의 코드를 복사해서 직접 테스트하시기 바랍니다

아래는 각 작업환경에 맞는 버전별 폴리필입니다. 필요하신 부분을 참고하시면 될 것 같네요.

 

ES6

 

ES5 + jQuery

 

HTML + CSS

...더보기

더 보기

참고가 되셨으면 좋겠습니다!

주말엔 아귀찜을 먹으러 평촌 학원가 근처에 있는 아귀찜 가게에 다녀왔습니다!

연평도 차림상이라는 가게인데 한 번 다녀온 후기를 간단하게 남겨볼까 해요.

그나저나 티스토리 에디터가 새롭게 출시되었는데 솔직히 아직까진 많이 불편하네요.. 돌려줘..!

가게의 전화번호 및 주소가 나와있다

 

전화번호 031-384-9333
전화번호2 010-5359-9333
위치 경기도 안양시 동안구 평촌동 921-9

 

밑에는 지도입니다. 지도가 보이지 않으시는 분들은 구글 맵 지도를 클릭해서 확인해보세요.

 

 

 

 

 

 

가게는 지하철역에서 조금 떨어져있어 지하철로 가기엔 조금 번거롭습니다.

주변에 버스가 많으니 평촌 학원가를 지나가는 버스를 타고 가면 쉽게 도착하실 수 있어요.

주차장은 제공하지 않는 듯 보였습니다. 가게 앞의 공간이 넓지는 않았어요.

 

연평도 차림상의 메뉴가 빼곡히 적혀있다

 

가게에서 판매하고 있는 메뉴들입니다!!

저희는 4명이서 방문했고 아구찜 대 자를 시켰어요. 4명정도면 대 자면 충분하다고 하시더라구요.

 

테이블은 나름 깔끔하다

 

테이블은 끈적거리지도 않고 무난했어요.

 

여러 유명인사들이 다녀간 흔적

 

벽 한 켠에는 이렇게 많은 유명인사들이 다녀간 흔적이 남아있습니다.

티비에도 방영된 집이라고 해서 그런지 많은 사람들이 다녀간 것 같았어요. 신기신기 ㅎ

 

언뜻 보기에도 엄청 푸짐해보이는 아구찜

 

주문한 아구찜이 나왔습니다~ 배가 너무 고파서 다른 밑반찬들 먹느라 사진을 못찍었네요. 

아구찜 대 자는 생각보다 많이 담겨져있었고 특히 콩나물이 수북~하게 담겨져 있었어요.

 

생선과 콩나물의 좋은 조합이다

 

저렇게 한 젓가락 떠서 먹으니 맛있더라구요! 근데.. 저한텐 정말 많이 매웠어요.

사장님이 덜 맵게 해주셨다고 했는데도 저는 물을 엄청 마실 정도로 많이 매웠어요!

매운거 잘 못 드시는 분들은 꼭 맵지 않은 맛으로 주문하세요!

어린이 메뉴에서 하얀 볶음밥을 주문하면 나오는 볶음밥, 상당히 맛있다.

 

중간에 밥이랑 같이 먹으려고 어린이 메뉴에서 하얀 볶음밥을 주문했어요.

생각보다 상당히 꼬들꼬들하고 달콤 고소해서 굉장히 맛있었어요!

아귀찜을 제외하고 따로 팔아도 될 정도로 저는 상당히 맛있게 먹었답니다.

 

김치에 같이 싸먹어도 맛있다

김치와 같이 곁들여서 먹어도 맛있어요! 

처음엔 굉장히 매웠는데, 그릇에 좀 덜어서 제법 식힌 후에 먹으니까 뜨겁지도 않고, 매운 맛도 많이 사라졌어요.

아귀찜은 조금 식혀서 먹는것도 굉장히 맛있네요 :)

 

다 먹고 난 후의 사진

다 먹을 수 있을까 싶었는데 결국 다 먹었네요.. ㅋㅋ

 

생각보다 양도 엄청 많고 배불러서 괜찮았습니다!

같이 간 일행 중에 외국인 친구도 있었는데, 그 친구도 같이 잘 먹고 나왔습니다!

다음에 기회가 되면 다시 한 번 방문해볼 생각이에요 ㅎㅎ 다들 한 번씩 가보시는 것도 좋을 듯 합니다! XD

 

오늘은 삼성역 주변에 있는 맛집을 소개해볼까합니다. 평소에 일식을 좋아하는 분들은 한 번쯤 방문하셔도 괜찮을 것 같아서 가져왔습니다!

가게 입구 모습


영업시간(연중무휴) - AM 11:30 ~ PM 22:00

예약문의 - 02-553-7870

팩스 - 02-553-7869

주소 - 서울특별시 강남구 대치동 949-1 번지 / 삼성역 3번출구에서 250m 오른편



가게 정면에서 바라본 모습입니다. 가게 간판은 깔끔하게 걸려있는 편이지만 벽면과 간판 배경이 줄무늬처럼 되어있어서 실제로 보면 길 건너편이나 조금 떨어져서 봤을 땐 잘 안보일 수도 있어요!

가게 입구엔 메뉴판이 비치되어 있어서 적당히 둘러볼 수도 있습니다. 

가게 입구에 들어가며 보이는 전경

2층에 위치한 어떤 룸


가게에 들어가면 가장 먼저 정면으로는 여러 종류의 사케를 모아놓은 전시장 같은 것이 보입니다. 가게에서 취급하는 사케뿐만이 아니라 여러 가지 다양한 종류를 모아놔서 구경할 재미는 있는 것 같아요. 가게 안으로 조금 더 들어가면 각 룸들이 보입니다. 저희는 예약한 곳이 2층에 위치해 있어 2층으로 올라갔는데, 다른 방에서 이미 식사중인 다른 손님들이 있더라구요. 신발을 벗고 들어가면 후에 화장실을 가거나 잠깐 나갔다오기 편하게 입구에 슬리퍼가 있고, 신발은 점원 분들이 가지런하게 정리를 해주셨어요. :)


룸 내부의 모습


식사를 하기 전에 찍었어야했는데.. 깜빡하고 나올 때 급하게 찍었네요. 룸 내부는 저렇게 여러 명이 앉아서 식사를 할 수 있게 되어있고, 기본적으로 좌식이지만 의자에 앉아서 식사를 할 수 있게 밑으로 공간이 뚫려 있습니다! 저는 양반다리를 하고 식사를 하는 게 많이 불편한데, 이런 면에선 그래도 편했네요.

생활의 달인에 나온 패


생활의 달인에도 나왔네요 ㅎ



디너 메뉴런치 메뉴

 



런치 메뉴와 디너 메뉴입니다. 저는 점심 회식으로 스시 정식을 시켰어요. 가게의 분위기 때문인지 다른 메뉴의 가격이 낮지는 않은 편입니다. 그래서 개인 단위로 오기엔 가격이 조금 부담 될 것 같아요. 대신 팀 회식으로 오면 괜찮을 것 같네요!

메뉴는 룸 안에 들어가면 테이블 옆에 비치되어 있습니다.

스시유 사시미 코스스시유 주말 정식


스시유 사시미 코스라고 있는데 이 메뉴들은 뭔가 좀 더 스페셜한 코스 요리같은 느낌으로 보면 될 것 같아요!


마실 것 종류스시유 조리장 추천사케


사케나 와인 종류도 제법 있으니 여기는 취향에 따라 고르시면 되겠죠 :)


식사 전에 나오는 에피타이저


드디어! 식전에 나오는 에피타이저의 모습입니다. 구성은 간장과 드레싱이 듬뿍 들어간 샐러드, 단무지 등 스시와 곁들여서 먹을 수 있는 것들과 푸딩처럼 생긴 부드러운 계란찜 같은 게 나왔는데요. 저 푸딩같은 게 정확히 뭔지는 모르겠지만 맛은 제법 부드럽고 괜찮았어요. 딱 식전에 먹기 좋았습니다.

푸딩처럼 생긴 에피타이저 한 스푼푸딩처럼 생긴 에피타이저


안에 새우까지 센스있게 들어있네요 :) 맛있었습니다.


삼치조림과 새우


그 다음으로 나온 에피타이저는 삼치조림과 새우입니다. 새우와 같이 있는건 브로콜리랑 전복 같이 보였는데 정확히는 모르겠어요. 새우는 부드럽고 삼치조림은 간이 살짝만 되어있는 느낌이었습니다. 간장에 살짝 찍어서 먹으니 괜찮았어요. 

개인적으로 맘에 들었던 점은, 위에 사진을 보시면 수저가 왼쪽에 놓여져있는데 사실 처음엔 오른쪽에 놓여져 있었어요. 저는 왼손잡이라서 왼쪽으로 위치를 바꿨는데, 음식을 놔주시는 직원분이 제가 왼손잡이인 것을 캐치하시고 삼치조림을 오른쪽에 놓아주셨네요. 다른 오른손잡이 분들 테이블을 보면 삼치조림이 왼쪽에 놓여져있더라구요. 이런 세심한 배려에 기분이 조금 좋았습니다 :D


스시 정식 풀샷

드디어 스시까지 나온 모습입니다! 제법 테이블이 넉넉해보이네요. 접시를 놓다가 스시 몇개가 위치가 흐트려졌네요.. 김말이, 계란말이, 오징어 등등 대체로 많이 익숙한 스시들이 나왔습니다!



김말이 스시참치 스시처럼 보이는 스시아마도 고등어스시


가운데 스시는 아마도 참치 스시인 것 같아요. 스시의 종류를 잘 몰라서 먹는데만 집중해버렸네요.. 가운데 스시는 그럭저럭 괜찮았습니다! 김말이 스시는 저한테는 조금 별로였어요. 생각보다 많이 퍽퍽하고 살짝 질긴 느낌이라서 물이나 국물을 중간에 좀 먹었던 기억이 있네요. 그리고 마지막 오른쪽 스시는.. 고등어 스시가 아닐까하는데, 생각보다 엄청 비렸어요. 생선 사이에 칼자국이 난 곳에 어떤 양념을해서 그런지는 모르겠지만 기대했던 것보단 많이 비려서 조금 먹는데 힘들었네요; 집에 돌아와서 검색해보니, 고등어를 초로 절인 시메사바 라고 부르는 초밥의 종류가 아닐까하는데 이 초밥은 특유의 향과 맛이 있다고하네요. 그래서 그렇게 느꼈던 걸지도 모르겠습니다!

그리고 초밥을 먹다보니 뭔가 계속 이상한 맛이 끝에 조금씩 감도는 느낌이 들길래 뭐지 뭐지했었는데, 한 번은 코끝이 찡-하게 울리는 거에요. 그래서 혹시나해서 초밥을 들어보니까.. 와사비가 안에 들어가있네요. 소량이지만 향과 맛을 느낄정도는 들어있었어요. 저는 와사비를 좋아하지 않아서 스시 먹을때도 와사비를 먹지는 않거든요. 이 부분은 조금 아쉬웠습니다.

그치만 스시 대부분은 맛이 좋았어요. 일반 스시뷔페에서 먹는 스시보다는 훨씬 더 고급스러웠습니다.


디저트로 나오는 우동디저트로 나오는 홍시


여기는 점원분들이 어떻게 다 아시고, 에피타이저나 스시를 다 먹을 때 쯤이면 문 열고 들어오셔서 다음 메뉴를 가져다 주시더라구요. 오랜 시간 많은 손님들에게 서비스를 제공하다보면 타이밍을 다 아시나봅니다.

스시를 다 먹고나면 우동이 나오는데요. 이 우동은 적당히 뜨겁고 따뜻해서 부담없이 편하게 바로 먹을 수 있었어요.

우동 다음엔 마지막으로 장식할 디저트로 홍시가 나오는데, 얼린 홍시를 상온에서 살짝 녹여서 가져온 것이 아닐까하는 생각이 들 정도로 굉장히 차가웠어요. 물도 따뜻한 보리차가 나오기도하고 우동을 바로 직전에 먹어서 밸런스를 맞추기 위해서 차가운 홍시가 나오는게 아닌가 싶기도하네요. 맛은 제법 달콤했습니다!

가격은 36,000원이었지만 구성이나 맛, 서비스를 생각해보면 제법 싸게 먹은 것 같아요. 바쁜 일상에 가끔 하루정도는 와서 점심으로 식사해도 괜찮다고 생각합니다. 대신, 예약이 많은 것 같으니 미리 예약을하고 가시는걸 추천드려요.

평소에 제가 정말 자주가는 보쌈집을 소개해드릴까 합니다! 삼성역 주변에서 식사하실 일이 있으면 꼭 한 번 가보시면 좋을 듯 하네요!


삼성역 지하철 1번출구삼성역 지하철 1번출구



2호선 삼성역 1번 출구로 나가세요.

삼성역 1번출구 나오면 보이는 거리

삼성역 1번출구 나오면 보이는 거리에서 골목길로 들어갔을 때



나가자마자 길을 따라서 약 50m 정도 직진 후 골목길로 들어가 다시 50m 정도 직진하시면 왼편에 건물이 있어요!

족발보쌈마을 2층



2층에 위치해 있고 비교적 찾기 쉬우니 금방 도착하실거에요.

족발보쌈마을 운영시간 및 메뉴



영업 시간 : 오전 11시 ~ 오후 10시 30분
전화 번호 : 02-567-2772

너무 늦게 가시면 족발, 보쌈이 다 떨어져있을 수도 있으니 적당한 시간대에 가시는게 좋을 것 같아요.


우수회원 인증



가게 입구엔 신선한 농산물을 사용하는 우수지점이라고 팻말을 걸어놨네요~

단체석 테이블

4인석 테이블



자리는 대부분이 4인석 테이블이고, 안 쪽에는 단체석도 앉을 수 있는 공간이 있습니다. 단체석 공간은 사진보다 더 넓어요!

메뉴 앞면

메뉴 뒷면



가게 메뉴판입니다. 음 뭐가 나름 많은 것 같긴 하지만 아무래도 가장 많이 주문하는 메뉴는 보쌈정식이랑 족발이 아닐까 싶어요! 저는 혼밥하러 왔기 때문에 보쌈정식을 주문했답니다 :D

보쌈정식 스크린샷



메뉴 구성은 4가지 반찬, 국이 같이 나와요. 반찬은 처음엔 제공되지만 더 먹고 싶다면 셀프바에서 가져와야 합니다. 깍두기랑 샐러드는 거의 항상 나오는 것 같고 나머지 2개는 그 때마다 다른 것 같아요. 국도 그 때 마다 달랐던 것 같네요~

현미밥으로 나오는 보쌈정식



저는 처음에 여기 왔을 때 깜짝 놀랐어요! 8,000원 정식에 현미밥을 주다니.. 요즘 한정식에 백반 나오는 곳도 8,000원 이상 받는 곳이 많은데 현미밥은 정말 센스있는 것 같네요 XD

보쌈 한 점 상세 스크린샷



고기는 비계와 고기가 적절히 섞어져 나옵니다. 운이 안 좋은 날에는 비계 비율이 더 높긴 하지만.. 또르르
대체로 적절히 잘 섞여있는 편이에요!

고기는 상당히 부드러운 편입니다. 정말 제일 맘에 들어요. 주문시켜 먹는 보쌈들과 비교가 안될 정도로 야들야들하고 아주 부드럽습니다 :D

김치와 보쌈을 같이



고기를 새우 젓갈에 살짝 묻혀 겉절이 김치와 같이 싸서 먹어도 맛있네요!


숙주나물 스크린샷



얘는 숙주나물인데요. 고기를 씹고 있다보면 아무래도 뭔가 더 입에 넣어주면 좋을 것 같은 느낌이 드는데 이 때 같이 먹으면 조화가 굉장히 잘 되는 느낌입니다. 메뉴 조합이 좋은 것 같아요!

보쌈정식에 나오는 국



국으로 입을 헹궈주는 것도 저는 잊지 않습니다 ㅎㅎ

밥, 보쌈, 김치 다 같이 먹는 스크린샷



이번엔 밥에 얹어서 한 번에 ㅎㅎ

반찬으로 나오는 깍두기



깍두기는 그렇게 맛있지는 않았지만 중간 중간 겉절이 대신 먹으니 나름 괜찮았어요 ~

나머지는 배고파서 먹는데 열중했네요 ㅎㅎ

혼밥하기도 좋고 회식하기도 좋은 것 같아요. 무엇보다 저는 혼밥할 때가 많은데, 혼자와서 4인석에 앉아도 눈치를 안 주는게 좋았네요 :-) 사실 근데 거의 4인석 밖에 없는..

여러분들도 한 번 드셔보시면 좋을 것 같아요!
추천합니다!

Git은 프로젝트나 파일 등을 관리할 때 빠져서는 안될 매우 중요한 시스템으로 자리잡았습니다. Git을 시작하기에 매우 쉽기도하지만 무엇보다도 다른 사람들과 협업을 해야할 때 진가를 발휘합니다. 


Git 이전에는 svn이나 ftp 등을 이용해서 파일을 관리했습니다. 그러나 근본적으로 이런 시스템들과 Git은 차별점이 존재했죠. 오늘날 가장 많이 사용되는 VCS(Version Control System)가 Git 임에는 누구도 부인할 수 없을 것입니다.


VCS 사용 빈도 구글 트렌드 결과VCS 사용 빈도 구글 트렌드 결과


구글 트렌드의 조사에 따르면, 2004년도부터 현재까지의 통계를 내보았을 때, 현재 가장 많이 사용되고 있는 VCS는 단연코 Git 입니다. 이 정도 수치면 시장 독점에 가까운 압도적인 비율이라고 볼 수 있겠네요. 


전 세계의 수 많은 사람이 사용하고 있는 Git 이지만, 사용하기 전에, 혹은 지금 사용하고 있더라도 더 원활하게 사용하기 위해서 알아두어야 할 것들이 있습니다.


브랜치를 가급적 많이 만드는 연습하기

연습단계에서 브랜치는 다다익선입니다. 많으면 많을수록 좋죠. 제가 지금보다 Git 에 대해서 더 많이 몰랐을 때는 부끄러운 이야기지만 git pull, git push, git commit 밖에 사용하지 않았습니다. 이 얼마나 단조로운 구조인가.. 게다가 브랜치는 무조건 master 에서 작업했으니 지금 돌이켜보면 프로젝트가 터지지 않고 어떻게 잘 버텼나 모를 정도네요. 실제 프로젝트에서는 브랜치를 무한대로 생성하는 것이 항상 좋지는 않습니다. 주로 구현할 이슈별로 브랜치를 만들거나, hotfix 를 만들던지 등의 나름의 팀 마다 갖고 있는 운영 전략을 따르게 됩니다. 그러나 연습 때는 무조건 많이, 될 수 있는 한 많이 만들어보는 것을 추천합니다.


브랜치를 생성하는 습관은 다음과 같이 들이기를 개인적으로 추천합니다.

  1. 다른 기능과 겹치는 점이 없는 독립적인 구현을 기준으로 브랜치를 생성합니다.

    1. 브랜치 전략이 익숙하지 않은 상태에서 서로 공유하는 기능을 갖고 있는 이슈로 브랜치를 만들어버리면 관리하는데에 어려울 수 있습니다. 

  2. master 에서 뿐만이 아니라 나무에 가지가 이리저리 자라듯이 브랜치에서 브랜치를 만들고, 또 브랜치를 만드는 연습을 합니다. 어떤 기능을 담당하는 브랜치에서 작업을 하다가 추가로 다른 기능을 붙이고 싶은데, 그 기능은 실험적으로 시도해보고 싶습니다. 그러면 master 로 돌아가지 말고 해당 작업 브랜치에서 새롭게 가지치기를 해서 브랜치를 만들어 작업합니다. 그 새로운 실험이 끝나면 원래 브랜치로 돌아와서 다시 작업합니다.

    1. master 에서 뿐만이 아니라 나무에 가지가 이리저리 자라듯이 브랜치에서 브랜치를 만들고, 또 브랜치를 만드는 연습을 합니다.


브랜치의 네이밍을 체계적으로 관리하기

브랜치를 만드는 연습이 좀 익숙해졌다면, 이제는 한 번쯤 자신이 어떤 패턴으로 브랜치들을 이름지었는지 돌아볼 필요가 있습니다. 좋은 브랜치는 그 이름마 보고도 어떤 작업을 진행 중인 브랜치인지 알 수 있어야 합니다.

Bootstrap에서 관리중인 브랜치 목록Bootstrap에서 관리중인 브랜치 목록

가령, tab 기능을 구현중인 브랜치 이름을 tab 이라고 지었습니다. 그런데 작업을 진행중에 이 탭을 카드지갑 형식과 네비게이션 바 형식으로 만들어보고 싶은 생각에 tab-1tab-2 라고 이름을 지어서 구분한다면, 시간이 흐른 뒤에 브랜치를 다시 봤을 때 어떤 브랜치인지 기억이 날까요? tab-card, tab-navbar 정도가 더 나을 것입니다. 

부트스트랩에선 브랜치들을 알기 쉽게 이름을 지어서 구분하고 있습니다. 이렇게 이름으로 브랜치를 구분하는 것도 좋은 습관입니다.

Fetch 와 Pull 의 차이점 이해하기

Git 에서 가장 중요한 부분 중에 하나라고 저는 생각합니다. Git 에 입문한 초보자들이 가장 많이 사용하는 명령어 중 하나는 단연코 git pull 일텐데, 리모트 브랜치나 다른 브랜치의 것을 가져와서 합치고 싶을 때 간혹 충돌이 일어나는 경우가 있습니다. 이 때 git fetchgit pull 의 차이만 이해하고 있어도 어느정도의 충돌은 예방할 수 있습니다. 


git fetch를 했을 때 브랜치 상황git fetch를 했을 때 브랜치 상황git pull을 했을 때 브랜치 상황git pull을 했을 때 브랜치 상황



git fetch 명령어와 git pull 명령어의 차이는 위 사진으로 설명 가능합니다. git fetch 명령어는 remote 브랜치의 최신 HEAD 정보를 가지고 오지만 현재 브랜치의 HEAD를 이동시키진 않습니다. 즉, 정보 업데이트만 합니다. 반면에 git pull 명령어는 remote 브랜치의 최신 HEAD 정보를 가지고 오면서 동시에 현재 브랜치의 HEAD를 맨 앞으로 이동시킵니다.


이게 왜 충돌 예방 방지를 할 수 있냐면, 단순히 원격 저장소에 있는 최신 정보만 업데이트 한 상태로 작업하고 싶다면, git fetch 를 사용하면 됩니다. 이렇게되면 현재 브랜치의 HEAD는 이동하지 않은 상태이므로, 현재 HEAD에서 새롭게 임시 브랜치를 생성하고 그 브랜치 위에 가져온 remote 브랜치의 커밋들 중에서 원하는 것만 cherry-pick 을 한다던지 등의 선택지도 생깁니다. 그리고 잘못 가져온 정보라고 할지라도 현재 HEAD 는 아무런 움직임을 취하지 않았기때문에 충돌이나 잘못 병합될 가능성 조차 없어지는 것입니다.


Merge 와 Rebase 의 차이점 이해하기

이 부분도 역시 Git 을 사용하는데 있어 필수적으로 알고 있어야할 개념이라고 생각합니다. 작업중인 브랜치에서 작업이 다 끝나 다른 브랜치로 합치고 싶은데, 어떤 것을 사용해야 할까요? 

정답은 없습니다.

merge를 사용하면 관리 포인트가 복잡해진다merge를 사용하면 관리 포인트가 복잡해진다




rebase는 라인을 단순하게 유지할 수 있다rebase는 라인을 단순하게 유지할 수 있다





현재 내가 속한 팀의 상황, 브랜치 운영 전략에 따라가는 것이 제일 좋습니다. 어느 한 쪽이 다른 한 쪽보다 항상 좋은 것은 아닙니다. git merge 명령어와 git rebase 명령어의 가장 큰 차이는 합친 후의 모습입니다. git merge 는 대상이 된 브랜치들을 그대로 간직할 수 있다는 장점이 있습니다. 사실 간직하고 있지 않아도 과거를 추적해서 원복할 수 있으니 큰 의미에서는 상관없겠지만, 사실 여간 귀찮은게 아니라.. git rebase 는 커밋들을 깔끔하게 관리하고 싶을 때 더욱 좋습니다. 

저는 사실 git rebase 명령어를 95% 이상의 비율로 사용하고 있습니다. git rebase -i HEAD~5 과 같은 명령어로 커밋 순서 변경, 커밋 합치기, 커밋 복사 등 대화형 인터페이스가 좋아서 사용하는 것도 있고, 문제 생겨도 지금의 저는 알 바 아니죠 ㅎ 미래의 내가 알아서 하겠지 라는 마음으로.. -_-ㅎ



Git 은 너무나도 편리한 시스템이지만 원리를 이해하지 않고 사용한다면 나만 편할 뿐 다른 사람들에겐 끝도 없는 고통을 주기 쉽습니다. 모두가 협업하는 상황에서는 Git 의 기본은 알고 작업하시면 좀 더 수월하겠죠? Git 은 제가 소개한 것보다 훨씬 더 많은 내용을 담고 있습니다만, 이 정도만 알고 가도 어지간한 작업은 문제가 없을거에요! 


오늘도 모두 즐코딩하세요~

안녕하세요 ~

이번엔 구글 애드센스가 승인난 후기를 알려드릴게요.


이 블로는 최초에 개설한건 제법 전이지만 관리를하고 있지 않아서 유령 블로그처럼 남은 곳이었는데, 최근에 다시 마음을 다 잡고 글을 조금씩 올려보고 있는 상황이었습니다. 그러던 와중에 블로그로 수익을 낼 수 있는 방법을 전해들었죠. 바로 애드센스! 


블로그를 네이버로 시작할지, 티스토리로 시작할 지 아니면 개인적으로 만들어 시작할 지 고민이 많았는데요. 저는 티스토리를 선택했습니다. 티스토리가 스킨 꾸미는 것도 그렇고 좀 더 이쁜 느낌이었어요.


그래서 신청한 구글 애드센스!

구글 애드센스 신청 거부당한 결과 스샷구글 애드센스 신청을 거부당한 결과 스크린샷


그러나... 결과는 거절


이유는 정확히 알려주지 않았습니다. 

구글 애드센스의 경우 신청은 자유지만 신청을 거절당했을 때 어째서인지 이유를 잘 알려주지 않더라구요. 심지어 검사를 사람이하는지 프로그램이하는지도 의문입니다. 


다른 블로그들을 살펴봐도 신청이 승인난 경우는 제각각인데요. 어떤 블로그는 글을 수십개를 써야지만 통과할 수 있었다는 곳도 있었고, 어떤 블로그는 글을 3-4개만 올리고도 승인받았다는 블로그도 있었습니다.


그 중에서 가장 인상 깊었던 블로그는 각 글마다 글자 수까지 계산해서 애드센스 승인을 받기 전까지 모든 글의 글자 수를 1,000자 이상이 되도록 맞췄다고 쓴 곳이 가장 인상 깊었네요.


어쨌든, 최초 신청을 거절당한 후에 블로그에 글을 하나 더 올리고 다시 도전..

그러나 다시 실패.. 이번엔 애드센스에 들어가보니 이유를 적어주네요. 이유는 컨텐츠 없음 이었습니다. 즉, 자기네들이 제공해주는 광고를 달만한 가치가 있는 글이 많이 없다는 이유가 거절 사유인 것 같습니다.


어떻게 해야하나 고민이 많이 되었는데, 2번이나 떨어졌으니까요. 일단 글을 더 올리자 하는 마음에 하나 두 개씩 다시 올리기 시작했습니다. 나름대로의 정성을 들여서 쓴 글도 넣어보구요. 그러고나서 얼마 후에 다시 도전했습니다.


구글 애드센스 승인 스크린샷구글 애드센스 승인 스크린샷


드디어!! 저도 첫 구글 애드센스를 승인받았습니다. 이 때 굉장히 뭔가 기쁘더라구요 대단한걸 한 건 아니지만 ㅎㅎ


아직까지 당연스럽게도 예상 수익은 $0 입니다만, 앞으로 1달 간격으로 수익을 꾸준히 기록해볼까합니다.

아직 구글 애드센스를 승인받지 못했거나 신청을 고민하고 계신 분들은 한 번 더 도전해보시기 바랍니다!



다 만들어진 결과 사진이번 실습을 통해서 만들어진 결과를 찍은 스크린샷


안녕하세요! 이번에는 CSS 에서 정말 정말 많이 사용되고 있는 Parallax 효과를 이용해서 표현할 수 있는 멋진 효과에 대해 설명드리려고 합니다.


먼저, 이 번 과정에서 사용되는 개념은 다음과 같습니다. 모르시는 분들은 기초 개념을 먼저 숙지하고 오시면 좋을 듯 합니다!


이번 효과는 간단하니 바로 시작할게요!

먼저 바탕이되는 div부터 만들어봅시다.


이미지 2장을 사용하기에 앞서, 각 이미지를 담을 div를 만들고 id 값을 각각 top, bottom 으로 정했습니다.

배경을 살짝 회색빛으로 만들어 분위기를 냈습니다. 


다음은 배경이 될 이미지를 로드합니다.


이미지가 뭔가 번잡해보이네요.

바로 background-image 로 입힌 이미지에 대한 추가 속성을 설정해주지 않았기 때문입니다.


이미지 추가 속성들을 적용시킨 결과를 보겠습니다.


결과가 제법 분위기 있게 나왔네요!

여기서 패럴랙스를 가능하게 하는 것은 background-attachment: fixed; 속성입니다. 이 속성을 사용해주면 스크롤을 할 때 스크롤에 딸려 올라가는게 아니라 뒷 배경이 천천히 움직이는 것처럼 동작합니다.


border-bottom 을 주어서 패럴랙스가 동작할 때 구분선을 넣어주었습니다. 이렇게해주니 스크롤 할 때 흑백으로 처리된 사진이 원본 사진으로 변하는 것처럼 보여서 아주 멋지네요!


이처럼 패럴랙스를 사용하면 간단하면서도 아주 멋진 효과를 기대할 수 있습니다.

그럼 오늘도 모두 즐코딩하세요~

+ Recent posts