20 | ```
21 |
22 | ```js
23 | new Vue({
24 | ...
25 | data: {
26 | status: false
27 | },
28 | methods: {
29 | showComment() {
30 | this.status = true;
31 | }
32 | }
33 | })
34 | ```
35 |
36 | ```css
37 | .fade-enter-active, .fade-leave-active {
38 | transition: opacity .5s;
39 | }
40 | .fade-enter, .fade-leave-to {
41 | opacity: 0;
42 | }
43 | ```
44 |
45 | 위 코드에서 show 버튼을 클릭하면 This is a comment 라는 텍스트가 부드럽게 살며시 표시됩니다. 여기서 주목할 코드는 ``이라는 태그입니다.
46 |
47 | `` 태그는 `name` 속성을 갖고 있고, 해당 `name` 속성과 연관된 CSS 코드가 위에 정의되어 있습니다. CSS 코드에 따라서 텍스트가 표시될 때 opacity의 값이 변화되고, 이에 따라 살며시 나타나는 효과를 볼 수가 있습니다.
48 |
49 | ## 트랜지션 클래스
50 |
51 | 앞의 코드에서 트랜지션 효과가 나타날 수 있었던 것은 바로 트랜지션 태그의 `name` 속성 덕택입니다. `name` 속성에 맞춰 아래와 같이 CSS 코드를 구현했었습니다.
52 |
53 | ```html {1}
54 |
55 |
56 | ```
57 |
58 | ```css {1,4}
59 | .fade-enter-active, .fade-leave-active {
60 | transition: opacity .5s;
61 | }
62 | .fade-enter, .fade-leave-to {
63 | opacity: 0;
64 | }
65 | ```
66 |
67 | `fade-enter-active`, `fade-leave-active` 등에서 볼 수 있듯이 뷰의 트랜지션 CSS 코드는 일정한 규칙을
68 | 갖습니다. 여기서 사용하는 CSS 클래스는 아래와 같이 6개가 있습니다.
69 |
70 | 
71 |
72 | 일반적으로 `v-enter`, `v-leave-to`를 함께 사용하고, `v-enter-to`, `v-leave`를 함께 사용합니다. 그림의 색깔을 보면 짝 지어져서 잘 구분되어 있습니다.
--------------------------------------------------------------------------------
/docs/composition/computed.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Computed API 🆕
3 | ---
4 |
5 | # computed API
6 |
7 | 컴퓨티드(computed) API는 컴포지션(Composition API)에서 사용된 [컴퓨티드 속성](/syntax/computed.html)을 의미합니다. 이 페이지에서는 컴포지션에서
8 | 컴퓨티드 속성을 어떻게 사용할 수 있는지 살펴보겠습니다.
9 |
10 | :::tip
11 | 컴퓨티드(computed) 속성은 뷰에서 자주 사용되는 속성입니다. 템플릿 표현식의 코드를 간결하게 해주고 API에서 받은 데이터를 가공할 때 연산 로직을 단순화 해줍니다.
12 | :::
13 |
14 | ## computed API 소개
15 |
16 | 인스턴스 옵션 속성 방식으로 작성한 컴퓨티드 속성 코드입니다.
17 |
18 | ```html
19 |
20 |
{{ reversedMessage }}
21 |
22 |
23 |
37 | ```
38 |
39 | 위 코드는 컴퓨티드 속성을 사용해 `Hello` 문자열의 순서를 뒤집어 `olleH`로 화면에 표시했습니다. 이 코드를 컴포지션 스타일로 작성하면 다음과 같습니다.
40 |
41 | ```html
42 |
43 |
{{ reversedMessage }}
44 |
45 |
46 |
63 | ```
64 |
65 | 컴포지션에서 컴퓨티드 속성을 쓰기 위해서는 먼저 위와 같이 `computed` API를 라이브러리에서 임포트해야 합니다. 그리고 위 `reversedMessage` 함수가 선언된 것처럼 `computed` API를 호출하고 안에 콜백으로 컴퓨티드 속성으로 정의될 값을 작성합니다. 작성된 코드를 보면 인스턴스 옵션 속성으로 작성한 코드와 크게 다르지 않습니다. 다만, `message.split()....` 형태가 아니라 `message.value.split()...`와 같이 `message.value` 값을 사용해야 합니다.
66 |
67 | :::tip
68 | 왜 `ref`로 선언된 값은 `setup` 함수 안에서 `.value`로 접근해야 할까요? 자세한 내용은 다음 링크를 참고하세요 :) [컴포지션의 .value](/reuse/composition.html#ref-두-번째-특징-value)
69 | :::
70 |
71 | ## 용어 정리
72 |
73 | - 컴퓨티드 속성 : Vue 2에서 사용하던 컴퓨티드 속성
74 | - 컴퓨티드 API : 컴포지션 스타일로 작성된 setup 안에서의 컴퓨티드 속성
--------------------------------------------------------------------------------
/docs/composition/event-emit.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Event Emit - setup() 🆕
3 | ---
4 |
5 | # 이벤트 발생 - setup()
6 |
7 | 이벤트 발생(event emit)은 하위 컴포넌트에서 상위 컴포넌트로 신호를 보내는 컴포넌트 통신 방식입니다. 컴포지션에서는 어떻게 컴포넌트 이벤트를 발생시킬 수 있는지 알아보겠습니다.
8 |
9 | ## 컴포넌트 이벤트 발생
10 |
11 | 기존에 인스턴스 옵션 속성으로 이벤트를 발생시키는 방법은 [$emit() API](/vue/event-emit)였습니다.
12 |
13 | ```html
14 |
23 | ```
24 |
25 | 위 코드는 하위 컴포넌트에서 상위 컴포넌트로 `change` 이벤트를 보내는 코드입니다. 이처럼 `$emit()` API를 이용하여 이벤트를 발생시킬 수 있습니다.
26 |
27 | ## setup()에서 컴포넌트 이벤트 발생시키기
28 |
29 | 이번엔 `setup()` 함수에서 이벤트를 발생시키는 방법을 알아보겠습니다.
30 |
31 | ```html
32 |
39 | ```
40 |
41 | 위 코드는 `setup()` 함수의 두 번째 파라미터 `context`를 이용하여 이벤트를 발생시킨 코드입니다. `setup()` 함수의 첫 번째 파라미터는 프롭스 속성을 의미하고 두 번째 파라미터는 이벤트를 발생시킬 수 있는 `emit`을 비롯하여 attrs, slots, expose 등을 가진 [컨텍스트 객체(Context Object)](https://vuejs.org/api/composition-api-setup.html#setup-context)를 의미합니다.
42 |
43 | 이 `context.emit()`은 기존 `$emit()`와 사용법이 동일합니다. 단순히 이벤트를 보낼 수도 있고 아래와 같이 이벤트의 인자를 넘길 수도 있습니다.
44 |
45 | ```js
46 | context.emit('change'); // change 이벤트만 발생
47 | context.emit('change', 100); // change 이벤트를 발생시키고 인자 100을 같이 넘김
48 | ```
49 |
50 | ## 이벤트 발생의 디스트럭처링 문법
51 |
52 | `context.emit()`은 다음과 같이 디스트럭처링 문법을 사용해도 됩니다.
53 |
54 | ```html
55 |
62 | ```
63 |
64 | 첫 번째 파라미터 `props`는 디스트럭처링을 썼을 때 [반응성이 사라지기 때문에 주의해야 했지만](/composition/props.html#디스트럭처링을-쓰고-싶다면), `emit`은 반응성이 주입된 값이 아니라 단순히 호출만 하는 API이기 때문에 반응성이 제거되는 부분을 걱정하지 않아도 됩니다.
--------------------------------------------------------------------------------
/docs/composition/img/afterToRefs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/composition/img/afterToRefs.png
--------------------------------------------------------------------------------
/docs/composition/script-setup.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Script Setup 🆕
3 | ---
4 |
5 | # `
36 | ```
37 |
38 | 위와 같이 컴포넌트 안에서 컴포지션으로만 컴포넌트의 로직을 작성하는 경우 아래와 같이 스크립트 태그에 `setup`을 추가해서 코드를 간결하게 작성할 수 있습니다.
39 |
40 | ```html{6}
41 |
42 |
{{ message }}
43 |
44 |
45 |
46 |
57 | ```
58 |
59 | 이 코드는 앞에서 살펴본 코드와 같은 코드입니다. 반응성이 주입된 `message` 데이터와 `changeMessage()` 함수는 별도로 익스포트하거나 추가적인 작업을 하지 않아도 템플릿 표현식에서 사용할 수 있게 됩니다. `setup()` 속성을 정의하면 매번 그 안에서 정의된 데이터와 메서드 등은 반환해줘야 하기 때문에 이 번거로운 절차를 줄여준 것이죠.
60 |
61 | ## 주의할 점
62 |
63 | `
78 | ```
79 |
80 | 위 코드에서 `message` 변수는 반응성이 주입되지 않은 일반 변수입니다. `let`으로 선언되었기 때문에 변수의 값을 바꿀 수 있죠. 변경 버튼을 눌러 `changeMessage` 메서드가 실행되면 `message` 값을 `Hello`로 바꿀 겁니다.
81 |
82 | 실제로 버튼을 눌러보면 스크립크 레벨에서는 `message`의 값이 바뀌지만 화면에는 바뀐 값이 표시되지 않는 것을 확인할 수 있습니다. 이렇게 동작하는 이유는 `message` 값에 반응성이 주입되지 않고 일반 변수로 취급된 상태에서 템플릿 표현식에 표시되었기 때문입니다. 그래서 함수가 정상적으로 실행되었지만 화면의 값은 바뀌지 않죠.
83 |
84 | 만약 값이 바뀌었을 때 반응성에 의해 정상적으로 변경된 데이터가 화면에 표시하게 하려면 다음과 같이 코드를 수정하면 됩니다.
85 |
86 | ```html
87 |
96 | ```
97 |
98 | 이처럼 `
35 | ```
36 |
37 | 위 코드는 `message` 값을 변경할 때마다 브라우저 콘솔에 변경된 값이 출력되는 코드입니다.
38 |
39 | 이번엔 위 코드를 컴포지션 스타일의 watch API로 변경해 보겠습니다.
40 |
41 | ```html
42 |
57 | ```
58 |
59 | 앞에서 살펴본 코드와 동일하게 변화를 감지할 데이터를 watch API의 첫 번째 인자로 선언하고, 두 번째 인자에 실행될 로직을 적었습니다.
60 |
61 | ## 용어 정리
62 |
63 | - watch 속성 : Vue 2에서 사용하던 watch 속성
64 | - watch API : 컴포지션 스타일로 작성된 setup 안에서의 watch 속성
--------------------------------------------------------------------------------
/docs/d3/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | orders: [d3.md, vue-with-d3, tutorial]
3 | ---
4 |
5 | [[toc]]
6 |
7 | toc
8 |
9 | [toc
10 | ]
--------------------------------------------------------------------------------
/docs/d3/images/d3-line-axis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/d3/images/d3-line-axis.png
--------------------------------------------------------------------------------
/docs/d3/images/d3-line-path.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/d3/images/d3-line-path.png
--------------------------------------------------------------------------------
/docs/d3/images/tutorial-npm-vue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/d3/images/tutorial-npm-vue.png
--------------------------------------------------------------------------------
/docs/deploy/cli3-rules.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: CLI v3.x Rules
3 | ---
4 |
5 | # 최신 뷰 CLI의 빌드 환경 규칙
6 |
7 | 이번 챕터에서는 뷰 CLI 버전 3 이상에서만 적용되는 몇 가지 빌드 환경 규칙을 알아보겠습니다.
8 |
9 | ## 애플리케이션 모드
10 |
11 | 뷰 CLI 3에서는 다음과 같이 크게 3가지 모드가 있습니다.
12 |
13 | - `development` : 프로토타이핑 및 개발 용도. `npm run serve` 명령어로 실행할 때의 모드
14 | - `production` : 상용화 및 배포 용도. `npm run build` 명령어로 실행할 때의 모드
15 | - `test` : 테스트 용도. `npm run test:unit` 명령어로 실행할 때의 모드
16 |
17 | 위의 모드는 `NODE_ENV` 변수 값과도 연관이 있습니다. 개발 모드로 실행한 경우에는 자동으로 `NODE_ENV` 값이 `development`로 설정이 되고, 배포 모드로 실행한 경우에는 `production`으로 설정됩니다.
18 |
19 | ## 애플리케이션 모드에 따라 뷰 CLI가 해주는 일?
20 |
21 | 뷰 CLI로 생성하여 애플리케이션을 제작하게 되면 기본적으로 웹팩을 사용합니다. 따라서, 모드에 따라 웹팩의 설정이 달라집니다. 예를 들면, 개발 모드일 때는 웹팩 데브 서버를 실행하기 때문에 빠르게 빌드 하면서 프로토타이핑 해나갈 수 있고 상용 모드일 때는 웹팩으로 빌드된 파일의 이름에 해쉬 값을 붙여 캐싱과 같은 이점을 누릴 수 있습니다.
22 |
23 | ## 환경 변수 파일 규칙
24 |
25 | 뷰 CLI 3에서는 프로젝트 루트 폴더에 아래와 같은 규칙으로 환경 변수 파일을 생성할 수 있습니다.
26 |
27 | ```bash
28 | .env # 모든 모드에 적용되는 환경 변수 파일
29 | .env.local # 모든 모드에 적용되나 개인만 사용하는 파일(git에 올라가지 않음)
30 | .env.[mode] # 특정 모드에 해당하는 환경 변수 파일
31 | .env.[mode].local # 특정 모드에 해당하지만 개인이 사용하는 파일(git에 올라가지 않음)
32 | ```
33 |
34 | 여기서 말하는 모드는 앞에서 살펴봤던 애플리케이션 모드를 의미합니다. 예를 들어, 상용 버전에 해당하는 환경 변수 파일은 아래와 같습니다.
35 |
36 | ```bash
37 | .env.production
38 | ```
39 |
40 | ## 환경 변수 파일 적용 순서
41 |
42 | 아래와 같이 개발 환경인지 배포 환경인지에 따라 여러 개의 환경 변수 파일을 분리할 수도 있습니다.
43 |
44 | - .env
45 | - .env.development
46 | - .env.production
47 |
48 | ```bash
49 | # .env
50 | VUE_APP_URL=https://domain.com
51 | ```
52 |
53 | ```bash
54 | # .env.development
55 | VUE_APP_URL=https://localhost:8080
56 | ```
57 |
58 | ```bash
59 | # .env.production
60 | VUE_APP_URL=https://123.123.123.123:9090
61 | ```
62 |
63 | 이때 배포 모드인 `npm run build` 명령어를 입력하게 되면 `.env.production` 파일의 내용이 가장 높은 우선순위를 갖게 됩니다. 따라서, 애플리케이션에서 사용한 `VUE_APP_URL` 값은 아래와 같이 설정됩니다.
64 |
65 | ```js
66 | // main.js
67 | console.log(VUE_APP_URL) // https://123.123.123.123:9090
68 | ```
69 |
70 | 위처럼 특정 모드에 해당하는 환경 변수의 파일의 값이 가장 높은 우선순위를 갖게 됩니다.
71 |
72 | :::tip
73 | 만약 `.env.development`, `.env.production` 등의 파일이 없다면 `.env` 파일에 설정한 값이 최종 값이 됩니다.
74 | :::
75 |
76 | ## 개발할 때는 로컬 환경 변수를 사용
77 |
78 | 앞의 규칙들을 인지한 상태에서 팀 공용으로 사용하는 환경 변수 이외에 개인적으로 환경 변수를 설정하여 사용할 수 있습니다.
79 |
80 | ```bash
81 | .env.development # 팀 공용 환경 변수
82 | .env.development.local # 개인 디버깅 또는 개발용 환경 변수
83 | ```
84 |
85 | 팀 공용으로 사용하는 애플리케이션 설정 값들은 `.env.development` 파일에 관리하고, 개인적으로 로컬 또는 특정 서버 디버깅을 위해 `.env.development.local` 파일에 관리하면 좋습니다.
86 |
87 | ```bash
88 | # .env.development
89 | VUE_APP_URL=http://internal.server.address:8080
90 | ```
91 |
92 | ```bash
93 | # .env.development.local
94 | VUE_APP_URL=http://localhost:8081
95 | ```
96 |
97 | 위와 같이 활용하는 경우 `.local` 파일은 git에도 올라가지 않기 때문에 편하게 API 엔드포인트(URL)를 바꿔서 사용할 수 있습니다.
98 |
--------------------------------------------------------------------------------
/docs/deploy/env-setup.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Environment Variables
3 | ---
4 |
5 | # 환경 변수 파일을 이용한 옵션 설정
6 |
7 | 환경 변수 파일이란 애플리케이션이 실행될 때 특정 값을 넘길 수 있는 변수를 의미합니다. 웹 애플리케이션 관점에서는 `.env` 파일에 정의된 변수를 의미하며 미리 정의된 값을 애플리케이션에서 활용하고 싶을 때 사용합니다.
8 |
9 | 이번 챕터에서는 Vue.js 애플리케이션에서 `.env` 파일을 활용하는 방법에 대해서 알아보겠습니다.
10 |
11 | ## env 파일
12 |
13 | CLI로 생성한 프로젝트로 개발 및 배포를 진행할 때 `.env`라는 환경 변수 파일로 옵션들을 편하게 바꿀 수 있습니다.
14 |
15 | ```bash
16 | # .env 파일
17 | VUE_APP_LOCAL_URI=http://localhost:8080/
18 | VUE_APP_TEST_SERVER=http://test.com:9090/
19 |
20 | clientId=client-test1234
21 | clientServer=server-test1234
22 | ```
23 |
24 | 위에서 설정한 변수들을 가지고 애플리케이션 로직에 활용할수도 있고, 웹팩으로 빌드를 할 때 위 변수의 내용을 반영할수도 있습니다.
25 |
26 | 예를 들어 아래와 같이 API의 호출 URL 값으로 `.env` 파일에 정의한 `VUE_APP_LOCAL_URI`를 사용할 수 있습니다.
27 |
28 | ```js
29 | // api/index.js
30 | function fetchUser() {
31 | return axios.get(`${VUE_APP_LOCAL_URI}users`);
32 | }
33 | ```
34 |
35 | 위와 같이 서비스 코드에서 `.env` 파일에 지정한 변수를 활용하려면 아래와 같이 웹팩에 추가 설정을 해줘야 합니다.
36 |
37 | ```js
38 | // webpack.config.js
39 | const webpack = require('webpack');
40 | const dotenv = require('dotenv');
41 | const env = dotenv.config().parsed;
42 |
43 | plugins: [
44 | new webpack.DefinePlugin({
45 | VUE_APP_LOCAL_URI: JSON.stringify(env.VUE_APP_LOCAL_URI),
46 | }),
47 | ],
48 | ```
49 |
50 | ## Vue CLI 3.x 버전의 환경 변수 파일 접근
51 |
52 | 최신 뷰 CLI에서는 위와 같이 웹팩의 DefinePlugin을 설정하지 않아도 환경 변수를 쉽게 접근할 수 있습니다. 환경 변수 파일의 변수 명을 아래와 같은 방식으로 정의하면 됩니다.
53 |
54 | ```bash
55 | VUE_APP_NUM=10
56 | VUE_APP_STR=hi
57 | ```
58 |
59 | 변수 명 앞에 항상 `VUE_APP_` 를 붙여주면 Vue CLI에서 내부적으로 웹팩 DefinePlugin을 활용하여 클라이언트 웹 자원에서 접근할 수 있게 설정됩니다.
60 |
61 | ```js
62 | // main.js
63 | import Vue from 'vue';
64 | // ...
65 |
66 | console.log(VUE_APP_NUM); // 10
67 |
68 | new Vue({
69 | // ...
70 | })
71 | ```
72 |
73 | ```html
74 |
75 |
76 |
77 |
78 |
79 |
86 | ```
87 |
88 | 이와 같은 방식으로 아래의 환경 변수들도 사용할 수 있습니다.
89 |
90 | - `NODE_ENV` : 애플리케이션 모드를 가리키는 변수 `development`, `production`, `test`
91 | - `BASE_URL` : `vue.config.js` 파일에 정의된 `publicPath`의 값과 동일한 변수
92 |
93 |
--------------------------------------------------------------------------------
/docs/deploy/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: General Guide
3 | ---
4 |
5 | # CLI로 생성한 프로젝트 배포하기
6 |
7 | Vue CLI로 생성한 프로젝트를 다 구현한 뒤 서버에 배포하는 방법에 대해서 다룹니다.
8 |
9 | ## 배포 명령어
10 |
11 | `npm run build`
12 |
13 | CLI로 생성한 프로젝트를 서비스에 배포하려면 제일 먼저 위 명령어를 실행해야 합니다. 실행하고 나면 아래와 같이 호스팅 할 수 있는 형태의 HTML, CSS, Javascript, 이미지 등의 파일이 생성되죠. 이렇게 생성된 자원을 빌드된 자원이라고 부릅니다.
14 |
15 | 
16 |
17 | ## 웹 서버에 빌드된 자원 배포하기
18 |
19 | 앞에서 생성한 빌드 자원을 각각의 서버에 배포하기 위해서는 각 서버에 추가적인 세팅이 필요합니다. 특히 뷰 라우터를 활용하여 싱글 페이지 애플리케이션을 제작하신 경우에는 서버에 꼭 페이지 fallback 옵션을 추가해주셔야 해당 url로 접근했을 때 정상적으로 동작합니다. 각 웹 서버의 fallback 설정 방법은 아래 공식 문서를 참고하세요.
20 |
21 | [Server Configuration Guide - Vue Router](https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations)
--------------------------------------------------------------------------------
/docs/design/pattern1.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Common Approach
3 | ---
4 |
5 | # 일반적인 컴포넌트 구조화 방식
6 |
7 | 첫 번째 컴포넌트 디자인 패턴은 일반적인 컴포넌트 구조화 방식입니다. 뷰 컴포넌트, 컴포넌트 통신 방식을 배우고 나면 자연스럽게 아래와 같이 컴포넌트를 구현하게 됩니다.
8 |
9 | ```html
10 |
11 |
12 |
13 |
14 |
15 |
16 |
29 | ```
30 |
31 | 위와 같은 방식은 등록된 컴포넌트가 여러 곳에 쓰이지 않을 때 사용하기 좋은 방식입니다. 실질적인 코드의 재사용성보다는 템플릿 코드의 가독성과 명시적인 코드의 모양새를 더 중점으로 두고 있습니다.
32 |
33 | 위의 코드가 잘 이해되지 않는다면 아래 챕터를 다시 살펴보시기 바랍니다.
34 |
35 | - [컴포넌트](/vue/components.html)
36 | - [컴포넌트 통신 방식](/vue/components-communication.html)
37 | - [프롭스 속성](/vue/props.html)
38 | - [이벤트 발생](/vue/event-emit.html)
--------------------------------------------------------------------------------
/docs/design/pattern2.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Building Controlled Components
3 | ---
4 |
5 | # 결합력 높은 컴포넌트
6 |
7 | 두 번째 컴포넌트 디자인 패턴의 핵심은 v-model 디렉티브입니다. v-model의 내부가 어떻게 동작하는지 이해하고 이를 응용하여 좀 더 결합력이 높은 컴포넌트를 만들어보겠습니다.
8 |
9 | ## v-model 디렉티브
10 |
11 | v-model 디렉티브는 양방향 데이터 바인딩을 지원하는 속성입니다. 양방향 데이터 바인딩이란 화면의 데이터와 뷰 인스턴스의 데이터가 항상 일치하는 것을 의미합니다. 코드를 보겠습니다.
12 |
13 | ```html
14 |
15 |
16 |
17 |
18 |
27 | ```
28 |
29 | 위 코드를 실행하면 인풋 박스의 초기 값으로 'hi'가 지정되어 있습니다. 그리고 인풋 박스의 내용을 바꾸면 inputText의 값도 같이 바뀝니다. 해당 내용은 뷰 개발자 도구에서 확인할 수 있습니다.
30 |
31 | ## v-model 디렉티브의 동작 방식
32 |
33 | 그럼 위에서 v-model 디렉티브가 어떻게 뷰 인스턴스의 데이터와 정보를 주고 받았을까요? v-model 디렉티브의 내부는 아래와 같은 구조로 동작합니다.
34 |
35 | ```html
36 |
37 |
38 |
39 |
40 |
49 | ```
50 |
51 | `inputText`의 값을 `:value`에 연결하고 인풋 박스의 입력을 모두 `$event.target.value`로 `inputText`에 넣습니다. 위의 코드는 v-model 디렉티브와 동일하게 동작합니다.
52 |
53 | ::: tip
54 | 위와 같은 코드는 커스텀 디렉티브를 작성할 때 많이 사용됩니다.
55 | :::
56 |
57 | ## v-model로 체크 박스 컴포넌트 만들기
58 |
59 | 앞에서 살펴본 v-model로 HTML 인풋 체크 박스를 컴포넌트화 해보겠습니다. 먼저 상위 컴포넌트인 App.vue의 코드입니다.
60 |
61 | ```html
62 |
63 |
64 |
65 |
66 |
67 |
81 | ```
82 |
83 | 이어서 하위 컴포넌트로 등록된 CheckBox.vue의 코드를 보겠습니다.
84 |
85 | ```html
86 |
87 |
88 |
89 |
90 |
91 |
101 | ```
102 |
103 | 위 코드를 실행하여 체크 박스를 클릭하면 `checked` 값이 정상적으로 true에서 false로 전환되는 것을 확인할 수 있습니다.
--------------------------------------------------------------------------------
/docs/design/pattern3.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Component with Slots
3 | ---
4 |
5 | # 슬롯을 이용한 컴포넌트 템플릿 확장
6 |
7 | 세 번째로 살펴볼 컴포넌트 디자인 패턴은 슬롯을 이용한 컴포넌트 설계 방법입니다. [슬롯](/reuse/slots.html) 챕터에서 이미 살펴봤듯이 슬롯은 하위 컴포넌트의 템플릿을 상위 컴포넌트에서 유연하게 확장할 수 있는 기능입니다.
8 |
9 | 슬롯은 탭, 모달(팝업), 버튼 등 흔히 사용되는 UI 컴포넌트를 모두 재사용 할 수 있게 도와줍니다. 예시 코드를 살펴보겠습니다.
10 |
11 | ```html {4}
12 |
13 |
14 |
17 |
18 | ```
19 |
20 | ```html {5,7-10}
21 |
22 |
23 |
32 |
33 |
34 |
43 | ```
44 |
45 | 위 코드는 버튼의 최소 마크업을 갖는 BaseButton 컴포넌트를 생성한 후 슬롯을 이용하여 버튼의 내용이 확장될 수 있게 구조화한 코드입니다. BaseButton.vue에서 `` 태그를 넣어놨기 때문에 BaseButton 컴포넌트를 등록하여 사용할 때 상위 컴포넌트에서 텍스트를 넣어 버튼의 이름만 바꾸거나, 텍스트와 아이콘을 함께 넣어 버튼의 UI를 꾸밀 수도 있습니다.
--------------------------------------------------------------------------------
/docs/design/pattern4.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Renderless Components
3 | ---
4 |
5 | # Pattern 4
6 |
7 | 업데이트 예정
--------------------------------------------------------------------------------
/docs/design/pattern5.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: High Order Components
3 | ---
4 |
5 | # 컴포넌트의 코드마저 재사용하는 하이 오더 컴포넌트
6 |
7 | ## 정의
8 |
9 | 뷰의 하이 오더 컴포넌트는 리액트의 하이 오더 컴포넌트에서 기원된 것입니다. 리액트의 [하이 오더 컴포넌트 소개 페이지](https://reactjs.org/docs/higher-order-components.html)를 보면 아래와 같이 정확한 정의가 나와 있습니다. *A higher-order component (HOC) is an advanced technique in React for reusing component logic*. 이 말을 정리해보면 다음과 같습니다.
10 |
11 | 하이 오더 컴포넌트는 **컴포넌트의 로직(코드)을 재사용하기 위한 고급 기술**입니다
12 |
13 | ## 반복되는 컴포넌트 로직
14 |
15 | 여기서 컴포넌트의 로직을 재사용한다는 말이 무슨 의미일까요? 이 표현에서 가리키고 있는 컴포넌트 로직이란 뷰에서 **인스턴스 옵션을 의미**합니다. 코드로 바로 살펴보겠습니다.
16 |
17 | ```html
18 |
19 |
20 |
21 |
50 |
51 |
52 |
63 | ```
64 |
65 | 위 코드는 ProductList 라는 컴포넌트와 UserList 컴포넌트의 로직을 정의한 코드입니다. 두 컴포넌트가 각각 상품과 사용자 정보를 서버에서 받아와 표시해주는 컴포넌트라고 가정했을 때, 공통적으로 들어가는 코드는 다음과 같습니다.
66 |
67 | ```js
68 | name: '컴포넌트 이름',
69 | mounted() {
70 | bus.$emit('off:loading');
71 | },
72 | ```
73 |
74 | `name`은 컴포넌트의 이름을 정의해주는 속성이고, `mounted()`에서 사용한 이벤트 버스는 서버에서 데이터를 다 받아왔을 때 스피너나 프로그레스 바와 같은 로딩 상태를 완료해주는 코드입니다. 이 두 컴포넌트 이외에도 서버에서 데이터 목록을 받아와 표시해주는 컴포넌트가 있다면 또 비슷한 로직이 반복될 것입니다.
75 |
76 | **이 때 이 반복되는 코드를 줄여줄 수 있는 패턴이 바로 하이 오더 컴포넌트입니다.**
77 |
78 | ## 하이 오더 컴포넌트로 반복 코드 줄이기
79 |
80 | 이 반복되는 코드를 줄이기 위해 하이 오더 컴포넌트를 구현해보겠습니다.
81 |
82 | ```js
83 | // CreateListComponent.js
84 | import bus from './bus.js'
85 | import ListComponent from './ListComponent.vue';
86 |
87 | export default function createListComponent(componentName) {
88 | return {
89 | name: componentName,
90 | mounted() {
91 | bus.$emit('off:loading');
92 | },
93 | render(h) {
94 | return h(ListComponent);
95 | }
96 | }
97 | }
98 | ```
99 |
100 | 위 코드는 CreateListComponent라는 하이 오더 컴포넌트를 구현한 코드입니다. 하이 오더 컴포넌트를 적용할 컴포넌트들의 공통 코드들(mounted, name 등)을 미리 정의했습니다. 그럼 이제 이 하이 오더 컴포넌트를 어떻게 사용할까요? 아래를 보겠습니다.
101 |
102 | ```js
103 | // router.js
104 | import createListComponent from './createListComponent.js';
105 |
106 | new VueRouter({
107 | routes: [
108 | {
109 | path: 'products',
110 | component: createListComponent('ProductList')
111 | },
112 | {
113 | path: 'users',
114 | component: createListComponent('UserList')
115 | },
116 | ...
117 | ]
118 | })
119 | ```
120 |
121 | 위와 같은 방식으로 하이 오더 컴포넌트를 임포트 하고, 각 컴포넌트의 이름만 정의를 해주면 컴포넌트의 기본 공용 로직인 `mounted()`, `name`를 가지고 컴포넌트가 생성됩니다. 따라서, 컴포넌트마다 불 필요하게 반복되는 코드를 정의하지 않아도 됩니다.
--------------------------------------------------------------------------------
/docs/es6+/async-await.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Async & Await
3 | ---
4 |
5 | # Async & Await
6 |
7 | 어싱크 어웨이트는 자바스크립트 비동기 처리 패턴의 최신 문법입니다. Promise와 Callback에서 주는 단점들을 해결하고 자바스크립트의 비동기적 사고 방식에서 벗어나 동기적(절차적)으로 코드를 작성할 수 있게 도와줍니다.
8 |
9 | ## 기본 문법
10 |
11 | async 함수의 기본 문법은 다음과 같습니다.
12 |
13 | ```js
14 | async function fetchData() {
15 | await getUserList();
16 | }
17 | ```
18 |
19 | async 함수는 함수의 앞에 `async`를 붙여주고 함수의 내부 로직 중 비동기 처리 로직 앞에 `await`를 붙여주면 됩니다. 좀 더 정확하게 말하면 Promise 객체를 반환하는 API 호출 함수 앞에 `await`를 붙입니다.
20 |
21 | ## 기본 예제
22 |
23 | 다음 코드를 보겠습니다.
24 |
25 | ```js
26 | async function fetchData() {
27 | var list = await getUserList();
28 | console.log(list);
29 | }
30 |
31 | function getUserList() {
32 | return new Promise(function(resolve, reject) {
33 | var userList = ['user1', 'user2', 'user3'];
34 | resolve(userList);
35 | });
36 | }
37 | ```
38 |
39 | `fetchData()` 함수에서 `getUserList()` 함수를 호출하고 나면 Promise 객체가 반환됩니다. 그리고 그 Promise는 실행이 완료된 상태(resolve)이며 실행의 결과로 `userList` 배열을 반환하고 있습니다. 따라서 `fetchData()`를 호출하면 `userList`의 배열이 출력됩니다.
40 |
41 | ```js
42 | fetchData(); // ['user1', 'user2', 'user3']
43 | ```
44 |
45 |
46 |
47 | ## (부록) Async & Await 문법을 사용하기 위한 바벨 플러그인 설정
48 |
49 | 뷰 최신 CLI 도구는 별도의 바벨 플러그인을 설치하지 않아도 바로 async & await 문법을 사용할 수 있습니다. 하지만, 뷰 CLI 2.x 버전대는 아래와 같은 추가 설정이 필요합니다.
50 |
51 | 1. 다음 바벨 플러그인을 설치한다.
52 |
53 | ```
54 | npm i babel-plugin-transform-runtime
55 | ```
56 |
57 | 2. .babelrc 파일에 아래와 같은 설정을 추가한다.
58 |
59 | ```
60 | "plugins": ["transform-runtime"]
61 | ```
--------------------------------------------------------------------------------
/docs/es6+/class.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/es6+/class.md
--------------------------------------------------------------------------------
/docs/es6+/const-let.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Const & Let
3 | ---
4 |
5 | # const & let
6 |
7 | const와 let 예약어는 ES6에서 사용하는 변수 선언 방식입니다.
8 |
9 | ## let
10 |
11 | let 예약어는 한번 선언하면 다시 선언할 수 없습니다.
12 |
13 | ```js
14 | // 똑같은 변수를 재선언할 때 오류
15 | let a = 10;
16 | let a = 20; // Uncaught SyntaxError: Identifier 'a' has already been declared
17 | ```
18 |
19 | ## const
20 |
21 | const 예약어는 한번 할당한 값을 변경할 수 없습니다.
22 |
23 | ```js
24 | // 값을 다시 할당했을 때 오류
25 | const a = 10;
26 | a = 20; // Uncaught TypeError: Assignment to constant variable.
27 | ```
28 |
29 | 단, 객체 `{}`또는 배열 `[]`로 선언했을 때는 객체의 속성(property)과 배열의 요소(element)를 변경할 수 있습니다.
30 |
31 | ```js
32 | // 객체로 선언하고 속성 값을 변경
33 | const b = {
34 | num: 10,
35 | text: 'hi'
36 | };
37 | console.log(b.num); // 10
38 |
39 | b.num = 20;
40 | console.log(b.num); // 20
41 | ```
42 |
43 | ```js
44 | // 배열로 선언하고 배열 요소를 추가
45 | const c = [];
46 | console.log(c); // []
47 |
48 | c.push(10);
49 | console.log(c); // [10]
50 | ```
51 |
52 | ## 블록 유효범위
53 |
54 | ES5의 var를 이용한 변수 선언 방식과 let & const를 이용한 변수 선언 방식의 가장 큰 차이점은 블록 유효범위입니다.
55 |
56 | ## var의 유효 범위
57 |
58 | var의 유효 범위는 함수의 블록 단위로 제한됩니다. 흔히 함수 스코프(function scope)라고 표현합니다.
59 |
60 | ```js
61 | var a = 100;
62 | function print() {
63 | var a = 10;
64 | console.log(a);
65 | }
66 | print(); // 10
67 | ```
68 |
69 | print 함수 앞에 선언한 `a`와 print 함수 안에 선언한 `a`는 각자 다른 유효 범위를 갖습니다.
70 | `var a = 100;` 는 자바스크립트 전역 객체인 window에 추가가 되고 `var a = 10;`는 `print()` 함수 안에서만 유효한 범위를 갖습니다.
71 |
72 | ## for 반복문에서의 var 유효 범위
73 |
74 | 위의 예제를 보다보면 흔히 헷갈릴 수 있는 부분이 "var의 유효 범위가 {}에 제한되나?" 입니다. 아래 예제를 살펴보겠습니다.
75 |
76 | ```js
77 | var a = 10;
78 | for (var a = 0; a < 5; a++) {
79 | console.log(a); // 0 1 2 3 4 5
80 | }
81 | console.log(a); // 6
82 | ```
83 |
84 | `var a = 10;`로 변수 a를 선언한 상태에서 for 반복문에 동일한 변수 이름 a를 사용했습니다. 이렇게 되면 `{}` 으로 변수의 유효 범위가 제한되지 않기 때문에 for 반복문이 끝나고 나서 `console.log(a);` 를 출력하면 for 반복문의 마지막 결과 값이 출력됩니다. 아마 자바나 다른 언어의 개발자들에게는 이 부분이 가장 헷갈릴 것입니다.
85 |
86 | 이러한 문제점을 해결하고 다른 언어와 통일감을 주기 위해 ES6에서는 const와 let의 변수 유효 범위를 블록`{}`으로 제한하였습니다.
87 |
88 | ## const와 let의 블록 유효범위
89 |
90 | 이번엔 위 반복문 코드에 var 대신 let을 적용해보겠습니다.
91 |
92 | ```js
93 | var a = 10;
94 | for (let a = 0; a < 5; a++) {
95 | console.log(a); // 0 1 2 3 4 5
96 | }
97 | console.log(a); // 10
98 | ```
99 |
100 | 반복문의 조건 변수 a를 let으로 선언하니 변수의 유효 범위가 for 반복문의 {} 블록 안으로 제한되었습니다.
--------------------------------------------------------------------------------
/docs/es6+/default-parameter.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Default parameter
3 | ---
4 |
5 | # 기본값 매개변수(Default parameter)
6 |
7 | 기본값 매개변수란 함수의 매개변수에 값이 전달되지 않았을 때 기본 값으로 설정하는 문법입니다.
8 |
9 | ## 구문
10 |
11 | 기본값 매개변수의 문법은 다음과 같습니다.
12 |
13 | ```js
14 | function functionName(param1 = defaultValue1, ..., paramN = defaultValueN) {
15 | // ...
16 | }
17 | ```
18 |
19 | 함수를 정의할 때 매개변수의 우측에 = 를 추가하고 함수에 값이 전달되지 않았을 때 설정할 기본 값을 defaultValue 위치에 지정합니다. 이 문법을 이용해 실제로 기본값 매개변수를 적용한 코드를 보겠습니다.
20 |
21 | ```js {1}
22 | function foo(param1 = 1, param2 = {}, param3 = 'korean') {
23 | console.log(param1, param2, param3);
24 | };
25 |
26 | foo(30, { name: 'amy' }, 'american'); // 30, { name: 'amy' }, 'american'
27 | foo(); // 1, {}, 'korean'
28 | ```
29 |
30 | ## 설명
31 |
32 | ES6 이전 문법에서는 or 연산자인 ||를 이용하여 입력받은 파라미터를 지역변수에 재정의하는 방식을 사용하였습니다. or 연산자의 특성으로 인하여 파라미터의 값이 [falsy value(false 로 평가되는 값, false, null, 0, undefined...)](https://developer.mozilla.org/ko/docs/Glossary/Falsy) 인 경우에 || 연산자 우항의 값을 기본값으로 사용하게 됩니다.
33 |
34 | ### || 를 이용한 기본값 할당 방식
35 |
36 | ```js {2-4}
37 | function printPersonInfo(height, weight, age) {
38 | var height = height || 180;
39 | var weight = weight || 60;
40 | var age = age || 66;
41 |
42 | console.log(height, weight, age);
43 | };
44 |
45 | printPersonInfo(10, 200, 300); // 10, 200, 300
46 | printPersonInfo(); // 180, 100, 66
47 | ```
48 |
49 | 기본값 매개변수를 사용하면 더는 함수 내부에서 값을 비교하고 할당하는 연산이 필요하지 않습니다.
50 | 간단하게 함수의 매개변수를 선언하는 곳에서 기본값을 미리 정의 할 수 있습니다.
51 |
52 | ### 기본값 매개변수 사용 방식
53 |
54 | ```js {1}
55 | function printPersonInfo(height = 180, weight = 60, age = 66) {
56 | console.log(height, weight, age);
57 | };
58 |
59 | printPersonInfo(178, 58, 29); // 178, 58, 29
60 | printPersonInfo(100); // 100, 60, 66
61 | ```
62 |
63 | ## 주의사항
64 |
65 | 하지만 ES6의 기본값 매개변수는 파라미터가 정의되지 않고 `undefined` 값인 경우에만 기본 값을 할당합니다.
66 | 따라서, || 연산자를 통한 지역변수 재정의 방식과는 그 동작이 다르므로, 예전 문법에 익숙한 경우 주의해서 사용하여야 합니다. 아래 예시 코드로 좀 더 상세히 알아보겠습니다.
67 |
68 | ```js
69 | // || 를 이용한 기본값 방식
70 | function printFruit(name, weight, price) {
71 | var name = name || 'apple';
72 | var weight = weight || 10;
73 | var price = price || 15000;
74 |
75 | console.log(name, weight, price);
76 | };
77 |
78 | printFruit(0, false, null); // apple, 10, 15000
79 |
80 |
81 | // 기본값 매개변수 사용 방식
82 | function printFruit(name = 'apple', weight = 10, price = 15000) {
83 | console.log(name, weight, price);
84 | };
85 |
86 | // 0, false, null을 값으로 인식하여 기본값 대체되지 않음
87 | printFruit(0, false, null); // 0, false, null
88 | // 값이 없거나 undefined 일 때 기본값으로 대체
89 | printFruit(); // apple, 10, 15000
90 | ```
91 |
--------------------------------------------------------------------------------
/docs/es6+/enhanced-object-literals.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Enhanced Object Literal
3 | ---
4 |
5 | # 향상된 객체 리터럴(Enhanced Object Literal)
6 |
7 | 향상된 객체 리터럴이란 기존 자바스크립트에서 사용하던 객체 정의 방식을 개선한 문법입니다. 자주 사용하던 문법들을 좀 더 간결하게 사용할 수 있도록 객체 정의 형식을 바꿨습니다.
8 |
9 | ## 기존 객체 정의 방식
10 |
11 | 기존 자바스크립트의 객체 정의 방식은 다음과 같습니다.
12 |
13 | ```js
14 | var josh = {
15 | // 속성: 값
16 | language: 'javascript',
17 | coding: function() {
18 | console.log('Hello World');
19 | }
20 | };
21 | ```
22 |
23 | ## 축약 문법 1 - 속성과 값이 같으면 1개만 기입
24 |
25 | 객체를 정의할 때 속성(property)와 값(value)이 같으면 아래와 같이 축약이 가능합니다.
26 |
27 | ```js
28 | var language = 'javascript';
29 |
30 | var josh = {
31 | // language: language,
32 | language
33 | };
34 |
35 | console.log(josh); // {language: "javascript"}
36 | ```
37 |
38 | 위와 같은 축약 문법을 뷰 컴포넌트 등록 방식과 뷰 라우터 등록 방식에 대입해보면 아래와 같습니다.
39 |
40 | ```js
41 | // #1 - 컴포넌트 등록 방식에서의 축약 문법
42 | const myComponent = {
43 | template: '
My Component
'
44 | };
45 |
46 | new Vue({
47 | components: {
48 | // myComponent: myComponent
49 | myComponent
50 | }
51 | });
52 | ```
53 |
54 | ```js
55 | // #2 - 라우터 등록 방식에서의 축약 문법
56 | const router = new VueRouter({
57 | // ...
58 | });
59 |
60 | new Vue({
61 | // router: router,
62 | router
63 | });
64 | ```
65 |
66 | ## 축약 문법 2 - 속성에 함수를 정의할 때 function 예약어 생략
67 |
68 | 기존에 객체를 정의할 때 객체의 속성에 함수를 연결하여 사용하는 경우가 많았습니다. 예를 들면 아래와 같이 말이죠.
69 |
70 | ```js
71 | const josh = {
72 | // 속성: 함수
73 | coding: function() {
74 | console.log('Hello World');
75 | }
76 | };
77 | josh.coding(); // Hello World
78 | ```
79 |
80 | 실제로 기능을 구현하다보면 위와 같이 정의해야 할 때가 너무 많습니다. 따라서, ES6에서는 아래와 같이 축약하여 코딩하는 것을 추천합니다.
81 |
82 | ```js
83 | const josh = {
84 | coding() {
85 | console.log('Hello World');
86 | }
87 | };
88 | josh.coding(); // Hello World
89 | ```
90 |
91 | 이렇게 `function` 예약어를 생략해도 동일하게 동작하기 때문에 타이핑 하는 시간이 줄어듭니다.
92 |
93 | 그럼 이번에는 뷰 코드에 적용해서 얼마나 타이핑이 줄어드는지 확인해볼까요?
94 |
95 | ```js
96 | new Vue({
97 | // ...
98 | methods: {
99 | fetchData: function() {
100 | // ...
101 | },
102 | showAlert: function() {
103 | // ...
104 | }
105 | }
106 | });
107 | ```
108 |
109 | 위의 코드를 아래와 같이 생략할 수 있습니다.
110 |
111 | ```js
112 | new Vue({
113 | // ...
114 | methods: {
115 | fetchData() {
116 | // ...
117 | },
118 | showAlert() {
119 | // ...
120 | }
121 | }
122 | });
123 | ```
--------------------------------------------------------------------------------
/docs/es6+/modules.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Modules
3 | ---
4 |
5 | # Import & Export
6 |
7 | 임포트(Import)와 익스포트(Export)는 자바스크립트의 코드를 모듈화 할 수 있는 기능입니다. 여기서 모듈화란 쉽게 말해서 다른 파일에 있는 자바스크립트의 기능을 특정 파일에서 사용할 수 있는 것을 의미합니다.
8 |
9 | ## 모듈화의 필요성
10 |
11 | 기본적으로 자바스크립트의 유효 범위는 전역으로 시작합니다. 따라서 아래와 같이 HTML 페이지를 로딩하면 원하는 결과가 나오지 않습니다.
12 |
13 | ```html
14 |
15 |
16 |
17 |
18 |
21 |
22 | ```
23 |
24 | ```js
25 | // a.js
26 | var total = 100;
27 | function getTotal() {
28 | return total;
29 | }
30 | ```
31 |
32 | ```js
33 | // b.js
34 | var total = 200;
35 | ```
36 |
37 | 다른 프로그래밍 언어 배경을 가지신 분들이라면 파일 마다 변수의 유효 범위가 다를 것이라고 생각합니다. 하지만 자바스크립트는 기본적으로 변수의 유효 범위가 전역으로 잡히기 때문에 네임스페이스 모듈화 패턴이나 [Require.js](https://requirejs.org/)와 같은 모듈화 라이브러리를 이용하여 모듈화 기능을 구현해왔습니다.
38 |
39 | 이제는 프로그래밍 패턴이나 별도의 모듈화 라이브러리를 사용하지 않고도 자바스크립트 언어 자체에서 모듈화를 지원합니다.
40 |
41 | ## import & export 기본 문법
42 |
43 | 모듈화 기능을 사용하기 위한 기본적인 import, export 문법을 보겠습니다.
44 |
45 | 먼저 export 문법입니다.
46 |
47 | ```js
48 | export 변수, 함수
49 | ```
50 |
51 | 다른 파일에서 가져다 쓸 변수나 함수의 앞에 `export` 라는 키워드를 붙입니다. 익스포트된 파일은 임포트로 불러와 사용할 수 있습니다.
52 |
53 | import 문법을 보겠습니다.
54 |
55 | ```js
56 | import { 불러올 변수 또는 함수 이름 } from '파일 경로';
57 | ```
58 |
59 | 익스포트된 변수나 함수를 `{}`에 선언한 뒤 해당 파일이 있는 경로를 적어줍니다.
60 |
61 | ## import & export 기본 예제
62 |
63 | 배운 문법을 바탕으로 간단한 예제를 살펴보겠습니다.
64 |
65 | ```js
66 | // math.js
67 | export var pi = 3.14;
68 | ```
69 |
70 | ```js
71 | // app.js
72 | import { pi } from './math.js';
73 |
74 | console.log(pi); // 3.14
75 | ```
76 |
77 | 위 코드는 `math.js`라는 파일에서 `pi`를 익스포트하고 `app.js` 파일에서 임포트하여 콘솔에 출력하는 코드입니다. 만약 변수가 아니라 함수를 내보내고 싶다면 아래와 같이 코딩할 수 있습니다.
78 |
79 | ```js {3-5}
80 | // math.js
81 | export var pi = 3.14;
82 | export function sum(a, b) {
83 | return a + b;
84 | }
85 | ```
86 |
87 | ```js
88 | // app.js
89 | import { sum } from './math.js';
90 |
91 | sum(10, 20); // 30
92 | ```
93 |
94 | 위 코드는 `math.js`에 두 숫자의 합을 구하는 `sum()` 함수를 익스포트 한 뒤 `app.js`에서 임포트하여 사용한 코드입니다.
95 |
96 | ## 브라우저 지원 범위
97 |
98 | ES6의 기본적인 문법들이 최신 브라우저에서 지원되는데 반해 import, export는 아직 보조 도구가 있어야만 사용할 수 있습니다. 브라우저 지원 범위는 [여기](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#Browser_compatibility)를 확인하세요.
99 |
100 | 가급적 실무 코드에서 사용하실 때는 [웹팩(Webpack)](https://webpack.js.org/)과 같은 모듈 번들러를 이용하여 구현하는 것을 추천드립니다.
--------------------------------------------------------------------------------
/docs/es6+/nullish-coalescing-operator.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Nullish coalescing operator
3 | ---
4 |
5 | # Nullish coalescing operator(`??`)
6 |
7 | 널 병합 연산자(Nullish coalescing operator)는 연산자(`??`)의 왼쪽 피연산자가 null 또는 undefined일 때 오른쪽 피연산자를 반환하고, 그렇지 않으면 왼쪽 피연산자를 반환하는 논리 연산자입니다.
8 |
9 | ## 기존 문자열 할당 방식
10 |
11 | 기존 함수의 인자가 null 또는 undefined인 경우를 판단하는 방식은 아래와 같았습니다.
12 |
13 | ```js
14 | function printTitle(text) {
15 | let title = text;
16 | if (text == null || text == undefined) {
17 | title = 'Cracking Vue.js';
18 | }
19 | console.log(title);
20 | }
21 |
22 | printTitle('Crack'); // Crack
23 | printTitle(); // Cracking Vue.js
24 | ```
25 |
26 | ## 널 병합 연산자를 이용한 방식
27 |
28 | 널 병합 연산자(`??`)를 이용한 위 예제 코드의 함수 정의 방식은 아래와 같습니다.
29 |
30 | ```js
31 | function printTitle(text) {
32 | let title = text ?? 'Cracking Vue.js';
33 | console.log(title);
34 | }
35 |
36 | printTitle('Crack'); // Crack
37 | printTitle(); // Cracking Vue.js
38 | ```
39 |
40 | 위 코드를 보시면 기존의 if 문을 활용해서 null 또는 undefined인 경우를 판단했던 방식보다 널 병합 연산자(`??`)를 사용했을 때 코드의 양이 줄어들고, 훨씬 간결해진 것을 확인하실 수 있습니다.
41 |
42 | ## 논리 연산자 OR(`||`)와의 차이점
43 |
44 | 널 병합 연산자(`??`)와 비슷한 논리 연산자 OR(`||`)가 있습니다. 논리 연산자 OR(`||`) 또한 왼쪽의 피연산자가 null 또는 undefined인 경우 오른쪽의 피연산자를 반환합니다.
45 |
46 | 하지만 논리 연산자 OR(`||`)는 null과 undefined를 포함한 [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) 한 값인 `0`, `''`, `NaN`의 경우에도 오른쪽 피연산자를 반환합니다.
47 |
48 | ```js
49 | function printTitle(text) {
50 | const title = text || 'Cracking Vue.js';
51 | console.log(title);
52 | }
53 |
54 | printTitle('Crack'); // Crack
55 | printTitle(); // Cracking Vue.js
56 | ```
57 |
58 | 논리 연산자 OR(`||`)는 `0`, `''`, `NaN` 과 같은 값을 유효한 값이라고 생각한 경우에는 문제가 발생합니다.
59 |
60 | 이런 문제가 발생했을 때 널 병합 연산자(`??`)를 사용하면 간단하게 해결할 수 있습니다.
61 |
62 | ```js
63 | function getCount(count) {
64 | return count || 'There is no record.';
65 | }
66 |
67 | getCount(0); // There is no record.
68 | getCount(1); // 1
69 | ```
70 |
71 | ::: tip
72 | 널 병합 연산자(`??`)와 논리 연산자 OR(`||`)를 상황에 따라 적절하게 사용하시면 아주 좋습니다.
73 | :::
74 |
--------------------------------------------------------------------------------
/docs/es6+/optional-chaning.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Optional Chaning
3 | ---
4 |
5 | # Optional Chaning(`?.`)
6 |
7 | 옵셔널 체이닝은 ES2020에 추가된 자바스크립트 문법입니다. 타입스크립트 `3.7` 버전에서 구현된 기능이기 때문에 타입스크립트를 사용하셨던 분들에겐 익숙한 문법일 것 입니다. 옵셔널 체이닝 문법으로 객체의 속성 값이 유효한지 검증할 수 있습니다. 옵셔널 체이닝(`?.`) **앞**의 평가 대상이 `undefined`나 `null`이면 에러가 발생하지 않고 `undefined` 를 반환합니다.
8 |
9 | ## 옵셔널 체이닝이 추가되기 전
10 |
11 | 옵셔널 체이닝 문법이 추가되기 전에 중첩된 객체의 하위 속성을 찾기 위한 코드는 아래와 같았습니다.
12 |
13 | ```js
14 | const userInfo = {
15 | name: {
16 | first: 'Hong',
17 | last: 'Gildong',
18 | },
19 | address: {
20 | city: 'Seoul',
21 | postcode: '04377',
22 | },
23 | };
24 |
25 | const postcode = userInfo.address && userInfo.address.postcode;
26 | ```
27 |
28 | `&&` 연산자를 사용해서 `userInfo` 에 `address` 속성이 있는지 확인을 하고 `postcode` 에 접근하는 것을 볼 수 있습니다.
29 |
30 | ## 옵셔널 체이닝이 추가 된 후
31 |
32 | ```js
33 | const userInfo = {
34 | name: {
35 | first: 'Hong',
36 | last: 'Gildong',
37 | },
38 | address: {
39 | city: 'Seoul',
40 | postcode: '04377',
41 | },
42 | };
43 |
44 | const postcode = userInfo.address?.postcode;
45 | ```
46 |
47 | 옵셔널 체이닝을 사용하면 이전 코드보다 문법적으로 깔끔하고 안전하게 중첩된 객체의 하위 속성 값에 접근할 수 있습니다.
48 |
49 | ```js
50 | const userInfo = {
51 | name: {
52 | first: 'Hong',
53 | last: 'Gildong',
54 | },
55 | address: {
56 | city: 'Seoul',
57 | postcode: '04377',
58 | },
59 | getInfo: () => userInfo,
60 | };
61 |
62 | userInfo.getInfo?.();
63 | // userInfo object
64 |
65 | userInfo.setInfo?.();
66 | // undefined
67 | ```
68 |
69 | 옵셔널 체이닝은 메소드의 존재 여부를 확인하고 호출할 때도 사용할 수 있습니다.
70 |
71 | ```js
72 | const userInfo = {
73 | name: {
74 | first: 'Hong',
75 | last: 'Gildong',
76 | },
77 | address: {
78 | city: 'Seoul',
79 | postcode: '04377',
80 | },
81 | };
82 | const key = 'city';
83 |
84 | console.log(userInfo.address?.[key]);
85 | // Seoul
86 | ```
87 |
88 | `.` 대신 대괄호 `[]` 를 사용해 객체의 속성에 접근하는 경우에도 옵셔널 체이닝을 사용할 수 있습니다.
89 |
90 | ## 널 병합 연산자(`??`)와 같이 활용하기
91 |
92 | 옵셔널 체이닝과 널 병합 연산자(`??`)를 사용하여 특정 속성의 값의 유무를 판별하고 기본 값을 정의할 수 있습니다.
93 |
94 | ```js
95 | const userInfo = {
96 | name: {
97 | first: 'Hong',
98 | last: 'Gildong',
99 | },
100 | address: {
101 | city: 'Seoul',
102 | postcode: '04377',
103 | },
104 | };
105 |
106 | const city = userInfo.address?.city ?? 'New York';
107 | ```
108 |
109 | 위 코드에서 `city`의 값은 널 병합 연산자(`??`)의 왼쪽 항인 옵셔널 체이닝으로 검증한 객체의 속성 값이 `null`또는 `undefined`이면 널 병합 연산자(`??`)의 오른쪽 항인 `New York`이 기본 값으로 적용됩니다.
110 |
111 | ::: tip
112 | 널 병합 연산자(`??`)에 대한 자세한 내용은 [널 병합 연산자(`??`) 포스팅](/es6+/nullish-coalescing-operator.html)을 참고하세요.
113 | :::
114 |
115 | ::: warning
116 | 옵셔널 체이닝을 남용하지 않도록 주의해주세요.
117 | :::
118 |
119 | 옵셔널 체이닝(`?.`)은 존재하지 않아도 괜찮은 대상에만 사용해야 합니다. 위 예제 코드에서 `userInfo`는 반드시 존재해야 하지만 `address`는 필수값이 아닙니다. 그러므로 `userInfo.address?.city` 이렇게 사용하는 것은 바람직하지만 `userInfo?.address?.city` 는 바람직하지 않습니다. 이렇게 사용하게 된다면 `userInfo`의 값이 올바르지 않을 때 즉시 에러를 발생시키지 못해 추후 디버깅에 어려움을 겪을 수 있습니다.
120 |
--------------------------------------------------------------------------------
/docs/es6+/spread-operator.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Spread Operator
3 | ---
4 |
5 | # 스프레드 오퍼레이터(Spread Operator)
6 |
7 | 스프레드 오퍼레이터는 한글로 번역해보면 펼침 연산자 정도로 볼 수 있습니다. 스프레드 오퍼레이터는 특정 객체 또는 배열의 값을 다른 객체, 배열로 복제하거나 옮길 때 사용합니다. 연산자의 모양은 `...` 이렇게 생겼습니다.
8 |
9 | ## 스프레드 오퍼레이터 사용법
10 |
11 | 바로 한번 코드로 살펴볼까요?
12 |
13 | ```js
14 | // obj 객체를 newObj 객체에 복제
15 | var obj = {
16 | a: 10,
17 | b: 20
18 | };
19 | var newObj = {...obj};
20 | console.log(newOjb); // {a: 10, b: 20}
21 |
22 | // arr 배열을 newArr 배열에 복제
23 | var arr = [1,2,3];
24 | var newArr = [...arr];
25 | console.log(newArr); // [1, 2, 3]
26 | ```
27 |
28 | 위 코드들은 모두 스프레드 오퍼레이터를 이용하여 특정 객체, 배열의 값을 각각 새로운 객체와 배열에 복제하는 코드입니다. 이렇게 스프레드 오퍼레이터를 사용하는 이유는 무엇일까요?
29 |
30 | ## 기존 자바스크립트의 객체 복제 방식
31 |
32 | 앞에서 살펴본 코드에서 스프레드 오퍼레이터를 사용하지 않고 기존 자바스크립트 문법으로만 구현해보겠습니다.
33 |
34 | ```js
35 | // 객체의 값을 복사하는 경우
36 | var obj = {
37 | a: 10,
38 | b: 20
39 | };
40 | var newObj = {
41 | a: obj.a,
42 | b: obj.b
43 | };
44 | console.log(newObj); // {a: 10, b: 20}
45 |
46 | // 배열의 값을 복사하는 경우
47 | var arr = [1,2,3];
48 | var newArr = [arr[0], arr[1], arr[2]];
49 | console.log(newArr); // [1, 2, 3]
50 | ```
51 |
52 | 객체를 복사하는 경우, 새로운 객체인 `newObj`에 새로운 속성들을 선언하고 각 속성에 `obj`의 속성들을 일일이 접근해서 대입해줘야 합니다. 배열 `newArr`의 경우에는 기존 배열 `arr`의 인덱스에 일일이 접근하여 새로운 요소를 만들어줘야 합니다.
53 |
54 | 위 코드에서 살펴볼 수 있듯이 스프레드 오퍼레이터를 사용하게 되면 타이핑해야 하는 코드의 양이 확연히 줄어듭니다.
55 |
56 | ## 뷰엑스에 적용해본 스프레드 오퍼레이터
57 |
58 | 뷰에서 스프레드 오퍼레이터가 가장 잘 활용되는 부분은 뷰엑스(Vuex)의 헬퍼 함수입니다. 뷰엑스와 헬퍼 함수가 익숙하지 않은 분들은 [Vuex 시작하기 2](https://joshua1988.github.io/web-development/vuejs/vuex-getters-mutations/)글을 참고하세요. 어차피 여기서 중요한 부분은 스프레드 오퍼레이터가 어떤 식으로 뷰에서 활용되는지 확인하는 것입니다.
59 |
60 | 그럼 코드를 살펴보겠습니다.
61 |
62 | ```js
63 | import { mapGetters } from 'vuex';
64 |
65 | export default {
66 | computed: {
67 | ...mapGetters(['getStrings', 'getNumbers', 'getUsers']),
68 | anotherCounter() {
69 | // ..
70 | }
71 | }
72 | }
73 | ```
74 |
75 | 위 코드에서 `mapGetters`라는 헬퍼 함수 앞에 스프레드 오퍼레이터인 `...`를 사용하였습니다. 만약 `...`를 사용하지 않았다면, computed 속성 함수에는 뷰엑스의 getters 속성 함수들만 정의할 수 있었을 것입니다. 무슨 말인지 이해가 안가신다구요? 그럼 위의 코드에서 `...`를 더 풀어볼게요.
76 |
77 | ```js
78 | import { mapGetters } from 'vuex';
79 |
80 | export default {
81 | computed: {
82 | getStrings() {
83 | // ..
84 | },
85 | getNumbers() {
86 | // ..
87 | },
88 | getUsers() {
89 | // ..
90 | },
91 | anotherCounter() {
92 | // ..
93 | }
94 | }
95 | }
96 | ```
97 |
98 | 만약 `...`를 쓰지 않았다면 `anotherCounter()`를 추가로 정의할 수 없었을 것입니다. `...`로 풀어서 위와 같이 삽입을 해줬기 때문에 추가로 computed 속성 함수를 정의할 수 있게 되었습니다.
--------------------------------------------------------------------------------
/docs/es6+/template-literal.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Template Literal
3 | ---
4 |
5 | # 템플릿 리터럴(Template Literal)
6 |
7 | 템플릿 리터럴이란 자바스크립트에서 문자열을 입력하는 방식입니다. 기존에는 `var str = 'Hello ES6'`와 같은 방식으로 사용하였으나 ES6에서는 백틱(back-tick)이라는 기호(\`)를 사용하여 정의합니다.
8 |
9 | ```js
10 | const str = `Hello ES6`;
11 | ```
12 |
13 | 위와 같이 백틱을 이용하게 되면 여러 줄에 걸쳐 문자열을 정의할 수도 있고, 자바스크립트의 변수를 문자열 안에 바로 연결할 수 있는 이점이 생깁니다.
14 |
15 | ## 여러 줄에 걸쳐 문자열 선언하기
16 |
17 | 기존 자바스크립트의 문자열 선언 방식인 작은 따옴표(')의 문제점은 아래와 같았습니다.
18 |
19 | ```js
20 | var str = 'Template literals are string literals allowing embedded expressions. \n' +
21 | 'You can use multi-line strings and string interpolation features with them. \n' +
22 | 'They were called "template strings" in prior editions of the ES2015 specification.';
23 | ```
24 |
25 | 작은 따옴표를 이용하여 긴 문자열을 선언하게 되면 자동으로 개행이 되지 않기 때문에 라인 브레이커(line breaker)인 `\n`를 개행할 곳 중간 중간에 추가해야 했습니다. 이렇게 되면 문장이 길면 길수록 `+`와 `\n`를 계속 추가해줘야 하는 번거로움이 생깁니다.
26 |
27 | 백틱을 이용해서 문자열을 선언하게 되면 위와 같이 개행할 필요가 없습니다.
28 |
29 | ```js
30 | const str = `Template literals are string literals allowing embedded expressions.
31 | You can use multi-line strings and string interpolation features with them.
32 | They were called "template strings" in prior editions of the ES2015 specification.`;
33 | ```
34 |
35 | 그럼 뷰에서는 위의 문법을 어떻게 적용할 수 있을까요? CDN 방식으로 뷰를 적용할 때 컴포넌트의 template 속성에 적용하면 좋습니다. 아래와 같이 말입니다.
36 |
37 | ```js
38 | Vue.component('my-cmp', {
39 | template: `
40 |
41 |
My Component
42 |
back-tick is useful
43 |
44 | `
45 | });
46 | ```
47 |
48 | ## 문자열 중간에 변수 바로 대입하기
49 |
50 | 기존 문자열 정의 방식에서 또 번거로웠던 부분은 바로 자바스크립트 변수 값을 문자열과 함께 사용하는 부분이었습니다.
51 |
52 | ```js
53 | var language = 'Javascript';
54 | var expression = 'I love ' + language + '!';
55 | console.log(expression); // I love Javascript!
56 | ```
57 |
58 | 위와 같이 문자열에 특정 변수의 값을 함께 사용하려면 `+`를 이용하여 문자열 중간에 해당 변수를 연결해줘야 했습니다.
59 |
60 | ES6에서는 템플릿 리터럴을 이용하면 아래와 같이 간편하게 문자열과 변수를 함께 사용할 수 있습니다.
61 |
62 | ```js
63 | var language = 'Javascript';
64 | var expression = `I love ${language}!`;
65 | console.log(expression); // I love Javascript!
66 | ```
67 |
68 | `${}`를 이용하면 위와 같이 변수의 값을 대입할 수 있을 뿐만 아니라 간단한 연산도 할 수 있습니다.
69 |
70 | ```js
71 | var language = 'Javascript';
72 | var expression = `I love ${language.split('').reverse().join('')}!`;
73 | console.log(expression); // I love tpircsavaJ!
74 | ```
75 |
76 | 위 코드는 `language`의 문자열 순서를 역으로 바꾸는 코드입니다.
--------------------------------------------------------------------------------
/docs/format/cli-questions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/format/cli-questions.png
--------------------------------------------------------------------------------
/docs/format/format-on-save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/format/format-on-save.png
--------------------------------------------------------------------------------
/docs/format/img/husky-prettier-error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/format/img/husky-prettier-error.png
--------------------------------------------------------------------------------
/docs/format/official.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ESLint & Prettier
3 | ---
4 |
5 | # ESLint와 Prettier 설정 하기
6 |
7 | 팀 프로젝트에서 뷰를 개발할 때 코드를 일관적으로 작성하고 에러를 덜 나게 하는 방법이 있습니다.
8 | 바로 ESLint + Prettier 플러그인을 사용하는 방법인데요.
9 | 이 2개 도구에 대한 자세한 설명과 설정 방법은 다음 글을 참고합니다.
10 |
11 | - [Vue.js 개발 생산성을 높여주는 도구 3가지](https://joshua1988.github.io/web-development/vuejs/boost-productivity/)
12 |
13 | ## Vite 기반 Vue 3 프로젝트에서의 ESLint와 Prettier 설정 방법
14 |
15 | Vue 3 공식 문서의 프로젝트 스캐폴딩(초기 환경 구성) 페이지에 안내된 프로젝트 생성 방법입니다.
16 |
17 | ```sh
18 | npm init vue@latest
19 | ```
20 |
21 | 이 명령어를 입력하면 Vite 기반의 프로젝트가 생성됩니다. 프로젝트가 생성될 때 프로젝트 이름, 타입스크립트 설정 등을 묻는 9개의 질문이 나옵니다. 각 질문을 아래와 같이 답합니다.
22 |
23 | 
24 |
25 | 생성된 프로젝트로 이동한 후 터미널(명령어 프롬프트 창)에 안내된 NPM 설치 명령어를 실행합니다. 설치가 완료된 후 VSCode의 설정에서 `Format on Save` 옵션을 켭니다.
26 |
27 | 
28 |
29 | 마지막으로 main.js 파일에서 `mount('#app')` 코드 끝에 세미 콜론을 붙이고 파일을 저장하면 프리티어 규칙에 따라 세미 콜론이 자동으로 사라지는 것을 볼 수 있습니다.
30 |
31 | ::: tip
32 | 코드 포맷팅에 대한 자세한 설정은 [프리티어 공식 문서](https://prettier.io/docs/en/options.html)의 내용과 [Vue.js 개발 생산성을 높여주는 도구 3가지](https://joshua1988.github.io/web-development/vuejs/boost-productivity/) 문서를 참고하세요.
33 | :::
34 |
--------------------------------------------------------------------------------
/docs/format/prettier.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Prettier
3 | ---
4 |
5 | # Prettier 설정 하기
6 |
7 | 협업 시, 에러를 줄이고 일관적인 코드를 작성하기 위해 코드 포맷터 프리티어(Prettier)를 사용하는 방법이 있습니다.
8 | Prettier를 프로젝트에 세팅하는 방법과 commit 전에 전체 코드를 대상으로 prettier를 실행해서 포맷팅 후에 commit을 하도록 하는 방법에 대해 알아보겠습니다.
9 |
10 | ## 포맷팅할 prettier 설정파일 생성하기
11 |
12 | 프로젝트 루트 디렉토리에서 `.prettierrc` 생성합니다.
13 |
14 | ```json
15 | {
16 | "singleQuote": true,
17 | "semi": true,
18 | "useTabs": false,
19 | "tabWidth": 2,
20 | "trailingComma": "all",
21 | "printWidth": 120,
22 | "parser": "babel",
23 | "bracketSpacing": true,
24 | "arrowParens": "avoid"
25 | }
26 | ```
27 |
28 | :::tip
29 | 각 옵션에 대해 자세히 알고 싶다면 [prettier 공식 사이트](https://prettier.io/docs/en/options.html)를 참고하세요.
30 | :::
31 |
32 | ## vscode에서 prettier 설치 및 실행하기
33 |
34 | 1. Extensions(mac : `command + shift + x`, window : `control + shift + x`)에서 Prettier를 설치합니다.
35 |
36 | 2. 설치가 완료되면 command + p를 누르고 `> Format Document`를 실행합니다.
37 |
38 | ## vscode에서 저장(command + s)시 자동으로 prettier 실행하기
39 |
40 | 1. command + ,를 누르고 검색에 `format on save`를 입력합니다.
41 |
42 | 2. `Editor: Format on Save`를 체크하고 vscode를 재부팅합니다.
43 |
44 | 3. 만약 저장시 포맷팅이 안된다면 다시 `command + ,`를 누르고 검색창에 `default formatter`를 검색하고 none으로 되어 있으면 Prettier - Code formatter로 변경 후 재부팅합니다.
45 |
46 | ## husky를 사용하여 commit 전에 prettier 전체 파일에 적용하기
47 |
48 | 허스키(husky)를 사용하면 Prettier를 설치하지 않아도 commit 전에 lint-staged에 설정된 파일 중에 변경된 파일을 대상으로 자동으로 포맷팅하여 commit 합니다.
49 |
50 | :::tip
51 | husky는 git hook을 쉽게 관리할 수 있는 도구입니다. git push, commit 등이 시행되기 전이나 후에 원하는 스크립트를 실행시켜줍니다.
52 | 더 자세한 내용을 알고 싶다면 [husky 공식 사이트](https://typicode.github.io/husky/)를 참고하세요.
53 | :::
54 |
55 | 1. 위에서 설명드린 `포맷팅할 prettier 설정파일 생성하기`을 만듭니다.
56 |
57 | 2. 관련 라이브러리를 설치합니다.
58 |
59 | ```sh
60 | yarn add -D husky@4 lint-staged prettier
61 | ```
62 |
63 | 3. package.json에 아래와 같이 추가합니다.
64 |
65 | ```json
66 | {
67 | ...
68 | "scripts": {
69 | ...
70 | "init-husky": "npx husky install .husky",
71 | "lint-front": "lint-staged"
72 | },
73 | "lint-staged": {
74 | // 변경된 js, vue, json, md 파일을 대상으로 prettier를 실행합니다.
75 | "src/**/*.{js,json,md}": [
76 | "prettier --write",
77 | "git add"
78 | ]
79 | },
80 | }
81 |
82 | ```
83 |
84 | 4. husky를 설치합니다.
85 |
86 | ```bash
87 | yarn init-husky
88 | ```
89 |
90 | 이후 생성된 `.husky` 파일 하위에 `pre-commit`이라는 파일을 하나 생성하고 아래 코드를 넣습니다.
91 |
92 | ```bash
93 | #!/bin/sh
94 | . "$(dirname "$0")/_/husky.sh"
95 |
96 | yarn lint-front
97 |
98 | ```
99 |
100 | 5. 이후 다른 파일을 변경하고 commit 하면 파일이 포맷팅 되어 commit 되는지 확인합니다.
101 |
102 | 6. 만약 아래와 같은 에러로 commit이 실패 한다면 터미널에 아래 코드를 넣습니다.
103 |
104 | 
105 |
106 | 깃 훅 실행 권한을 부여하는 코드입니다.
107 |
108 | ```sh
109 | chmod ug+x ./husky/*
110 | chmod ug+x .git/hooks/*
111 | ```
112 |
113 | 7. vscode를 재부팅하고 다시 파일을 변경하여 commit 합니다.
114 |
--------------------------------------------------------------------------------
/docs/js/array.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Array
3 | ---
4 |
5 | # 배열(Array)
6 |
7 | 배열(Array)은 객체와 더불어 실제로 웹 애플리케이션을 구현할 때 가장 많이 쓰이는 변수 타입입니다. 배열을 선언하는 방식은 객체와 유사합니다.
8 |
9 | ```js
10 | var arr = [];
11 | ```
12 |
13 | 여기서 `[]`를 배열 리터럴이라고 하며 배열을 정의할 때 사용합니다.
14 |
15 | ## 배열의 요소
16 |
17 | 객체는 `속성 / 값`의 조합으로 데이터를 저장하지만 배열은 `인덱스 / 값`의 조합으로 데이터를 저장합니다.
18 |
19 | ```js
20 | // arr 변수에 빈 배열을 선언
21 | var arr = [];
22 |
23 | // 배열의 0번째 인덱스에 10을 대입
24 | arr[0] = 10;
25 |
26 | console.log(arr); // [10]
27 | ```
28 |
29 | ::: tip
30 | 배열의 인덱스는 0부터 시작합니다. 빈 배열에 최초로 값을 추가하면 0번째 인덱스에 값이 추가됩니다.
31 | :::
32 |
33 | ## 배열 조작하기
34 |
35 | 배열을 조작하는 방법은 아래와 같이 직접 인덱스에 접근해서 조작하는 방법이 있습니다.
36 |
37 | ```js
38 | // 인덱스를 직접 접근해서 사용하는 경우
39 | var arr = [];
40 | arr[0] = 100;
41 | arr[1] = 20;
42 | arr[0] = 10;
43 | console.log(arr); // [10, 20]
44 | ```
45 |
46 | 하지만 위와 같이 인덱스에 직접 접근해서 조작하는 것보다 자바스크립트 내장 API를 사용하는 것을 추천합니다. 그리고 이 방식은 뷰 개발자 뿐만 아니라 자바스크립트 개발자들에게도 권장되는 방식입니다.
47 |
48 | ```js
49 | // 자바스크립트 내장 API를 사용하는 경우
50 | var arr = [];
51 | arr.push(100); // [100]
52 | arr.push(20); // [100, 20]
53 | arr.splice(0, 1, 10); // [10, 20]
54 | console.log(arr); // [10, 20]
55 | ```
56 |
57 | ## 자주 사용하는 배열 API
58 |
59 | 배열을 조작할 때 주로 사용하는 API는 다음과 같습니다.
60 |
61 | - [push()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) : 배열에 데이터 추가 (맨 끝 인덱스부터 추가됨)
62 | - [slice()](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) : 배열의 특정 인덱스에 있는 값을 반환 (배열의 내용이 변환되지 않음)
63 | - [splice()](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) : 배열의 특정 인덱스에 있는 값을 변경 또는 삭제 (배열의 내용이 변경됨)
64 | - [pop()](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/pop) : 배열의 마지막 인덱스의 값을 꺼냄 (배열의 내용이 변경됨)
65 | - [shift()](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) : 배열의 첫번째 인덱스의 값을 꺼냄 (배열의 내용이 변경됨)
66 |
--------------------------------------------------------------------------------
/docs/js/condition.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Condition
3 | ---
4 |
5 | # 조건문(Condition)
6 |
7 | 조건문은 주어진 조건을 만족할 때만 코드를 실행하게 하는 문법입니다. 조건에 따라서 다른 동작을 실행하고 싶을 때 조건문을 사용합니다. 특정 기능을 구현할 때는 여러 가지 케이스를 고려해서 코드를 작성해야 하므로 변수 다음으로 가장 많이 사용되는 문법입니다.
8 |
9 | 조건문에 대한 이해를 돕기 위해 간단한 예시를 살펴보겠습니다. 영화 티켓을 끊을 때 성인이면 10,000원을 내고 학생이면 5,000원을 내야 하는 결제 로직을 작성한다고 하면 다음과 같습니다.
10 |
11 | ```
12 | 만약, 나이가 18세 이상이면 10,000원을 낸다.
13 | 그렇지 않고 나이가 17세 이하면 5,000원을 낸다.
14 | ```
15 |
16 | 위 로직을 조건문으로 작성해 보면 다음과 같습니다.
17 |
18 | ```js
19 | var age = 24;
20 | if (age >= 18) {
21 | console.log('10,000원 지불');
22 | } else {
23 | console.log('5,000원 지불');
24 | }
25 | ```
26 |
27 | ## if 조건문
28 |
29 | if 문은 주어진 조건식이 참이면 if 문 블록 안에 있는 코드를 실행합니다.
30 |
31 | ```js
32 | if (조건식) {
33 | // 조건식이 맞으면 이 블록 안의 코드가 실행됩니다.
34 | }
35 | ```
36 |
37 | 위 문법을 이해하기 위해 간단한 예제를 보겠습니다.
38 |
39 | ```js
40 | var a = 10;
41 | if (a === 10) {
42 | console.log('10이 맞습니다');
43 | }
44 | ```
45 |
46 | 위 코드는 변수 `a`에 `10`이 할당되어 있으므로 if 문 안의 코드인 `console.log('10이 맞습니다')`가 실행됩니다. 이렇게 블록 안의 코드가 한 줄이든 여러 줄이든 블록 안의 코드는 모두 실행됩니다.
47 |
48 | ## if else 문
49 |
50 | 위에서 배운 if 문에서 조건식이 참이 아니라 거짓인 경우에 실행될 코드도 아래와 같이 작성할 수 있습니다.
51 |
52 | ```js
53 | if (조건식) {
54 | // 조건식이 맞으면 이 블록 안의 코드가 실행됩니다.
55 | } else {
56 | // 위 조건식이 틀리면 이 블록 안의 코드가 실행됩니다.
57 | }
58 | ```
59 |
60 | 위 문법을 이용하여 앞 예제에 else 구문을 추가해 보겠습니다.
61 |
62 | ```js
63 | var a = 30;
64 | if (a === 10) {
65 | console.log('10이 맞습니다');
66 | } else {
67 | console.log('a가 10이 아니면 이 코드가 실행됩니다');
68 | }
69 | ```
70 |
71 | 위 코드에서 변수 `a`의 값이 `10`이 아니기 때문에 `else` 문 블록 안의 코드가 실행됩니다. 최종적으로 `a가 10이 아니면 이 코드가 실행됩니다` 가 출력됩니다.
72 |
73 | ## else if 문
74 |
75 | else if 문은 조건식이 여러 개인 경우 사용할 수 있는 문법입니다. 기본 문법은 다음과 같습니다.
76 |
77 | ```js
78 | if (첫번째조건식) {
79 | // 첫번째조건식이 맞으면 이 블록 안의 코드가 실행됩니다.
80 | } else if (두번째조건식) {
81 | // 두번째조건식이 맞으면 이 블록 안의 코드가 실행됩니다.
82 | }
83 | ```
84 |
85 | 위에서 조건문을 소개할 때 사용했던 조건을 다시 들고 와서 시나리오를 좀 더 추가해 보겠습니다.
86 |
87 | ```
88 | 만약 나이가 65세 이상이면 돈을 내지 않는다.
89 | 만약 나이가 18세 ~ 65세 사이면 10,000원을 낸다.
90 | 그렇지 않고 나이가 17세 이하면 5,000원을 낸다.
91 | ```
92 |
93 | 위 조건을 코드로 작성해 보면 다음과 같습니다.
94 |
95 | ```js
96 | var age = 24;
97 | if (age > 65) {
98 | console.log('무료');
99 | } else if (18 <= age && age <= 65) {
100 | console.log('10,000원 지불');
101 | } else {
102 | console.log('5,000원 지불');
103 | }
104 | ```
105 |
106 | 위 코드에서 `age` 값이 24이므로 18세 이상이고 65세 이하인 else if 구문에 해당됩니다. 따라서, `else if` 문 블록 안에 있는 코드가 실행되어 `10,000원 지불`이 출력됩니다.
107 |
108 | ## switch 문
109 |
110 | switch 문은 조건 식이 여러 개인 경우에 사용할 수 있는 조건문입니다. 바로 앞에서 배운 else if 구문과 역할이 같습니다. 기본 문법은 다음과 같습니다.
111 |
112 | ```js
113 | switch (변수) {
114 | case 값1:
115 | // 변수가 값1이면 실행할 로직
116 | break;
117 | case 값2:
118 | // 변수가 값2이면 실행할 로직
119 | break;
120 | default:
121 | // 변수가 위 어떤 케이스에도 해당하지 않는 경우 실행할 로직
122 | }
123 | ```
124 |
125 | a의 값에 따라 출력값이 달라지는 간단한 예제 코드를 보겠습니다.
126 |
127 | ```js
128 | var num = 33;
129 | switch (num) {
130 | case 10:
131 | console.log('10입니다');
132 | break;
133 | case 33:
134 | console.log('33입니다');
135 | break;
136 | default:
137 | console.log('10과 33이 아닌 다른 숫자입니다.');
138 | }
139 | ```
140 |
141 | 위 코드가 실행되면 `33입니다`가 출력됩니다. 여기서 만약 `num` 값이 20이라면 `default` 블록에 정의한 `10과 33이 아닌 다른 숫자입니다.`가 출력됩니다.
142 |
--------------------------------------------------------------------------------
/docs/js/function.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Function
3 | ---
4 |
5 | # Function
6 |
7 | 함수란 특정 기능을 수행하는 코드의 단위입니다. 좀 더 쉽게 설명하자면 코드 여러 줄이 모여 있는 코드 모음 정도가 되겠네요. 이 코드의 모음은 반드시 1개 이상의 목적이 있어야 합니다.
8 |
9 | 함수는 아래와 같이 생겼습니다.
10 |
11 | ```js
12 | function sumNumbers(a, b) {
13 | return a + b;
14 | }
15 | ```
16 |
17 | 위의 `sumNumbers` 라는 함수는 두 개의 숫자를 넘겨 받아 합을 반환해주는 역할을 하고 있습니다. 사용하려면 아래와 같이 호출합니다.
18 |
19 | ```js
20 | sumNumbers(10, 20); // 30
21 | ```
22 |
23 | ## 함수 표현식과 함수 선언문
24 |
25 | 다른 프로그래밍 언어와 다르게 자바스크립트는 함수를 선언하는 방법이 2가지가 있습니다.
26 |
27 | - 함수 표현식
28 | - 함수 선언문
29 |
30 | 각각의 형식을 코드로 살펴보겠습니다.
31 |
32 | ```js
33 | // 함수 표현식
34 | var sumNumbers = function(a, b) {
35 | return a + b;
36 | };
37 |
38 | // 함수 선언문
39 | function sumNumbers(a, b) {
40 | return a + b;
41 | }
42 | ```
43 |
44 | 일반적으로 많은 프로그래밍 가이드에서 함수 선언문을 더 추천하고 있지만, 개인 스타일에 따라서 얼마든지 선택하여 사용할 수 있습니다.
45 |
46 | ::: tip
47 | 함수 표현식과 같이 함수를 정의할 수 있는 이유는 자바스크립트에서는 함수도 변수나 인자로 취급할 수 있기 때문입니다. [일급 객체(first-class citizens)](https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function)라는 특징에 대해서 알아보시면 좋을 것 같네요!
48 | :::
49 |
50 | ## 함수형 사고와 함수형 프로그래밍
51 |
52 | 함수를 작성하실 때는 가급적 [단일 책임 원칙(Single Responsibility Principle)](https://en.wikipedia.org/wiki/Single_responsibility_principle)을 지켜주시면 좋습니다. 단일 책임 원칙이란 1개의 함수는 1개의 기능만 담당해야 한다는 프로그래밍 원칙입니다. 함수에 여러 가지 기능이 들어가면 들어갈수록 재사용하기가 어려워지며 이는 Vue.js 컴포넌트를 설계할 때도 사고 방식에 영향을 줄 수 있습니다.
53 |
54 | 단일 책임 원칙의 관점에서 잘 설계된 함수와 그렇지 않은 함수를 비교해보겠습니다.
55 |
56 | ```js
57 | // 잘 설계된 함수
58 | function sumNumbers(a, b) {
59 | return a + b;
60 | }
61 | ```
62 |
63 | 위 함수는 함수 명에서도 알 수 있지만 두 수의 합을 구하는 간단한 함수입니다. 두 개의 숫자를 더하는 행위에만 관심을 가지고 있고, 덧셈 로직을 제외한 나머지 코드는 넣지 않았습니다.
64 |
65 | 아래는 같은 함수 명으로 다른 로직을 수행하는 함수입니다.
66 |
67 | ```js
68 | // 단일 책임 원칙에 벗어나는 함수
69 | function sumNumbers(a, b) {
70 | var num = 1000;
71 | var data = {};
72 |
73 | $.get('domain.com/products/1').then(function(response) {
74 | data = response.data;
75 | });
76 |
77 | var total = a + b + num;
78 | return total;
79 | }
80 | ```
81 |
82 | 위 함수의 이름은 `sumNumbers`라는 함수의 이름을 갖고 있지만, 실제로 두 수를 더하는 로직 이외에도 데이터 요청이나 다른 숫자를 더하는 로직들이 뒤엉켜 있습니다. 위와 같은 코드는 단일 책임 원칙에 벗어나는 코드이며 재사용하기가 쉽지 않습니다.
83 |
84 | 좀 더 나아가서 앞의 함수를 단일 책임 원칙의 관점에서 리팩토링 해보면 아래와 같이 바뀔 수 있습니다.
85 |
86 | ```js {2,6,12}
87 | // 함수 리팩토링
88 | function sumNumbers(a, b) {
89 | return a + b;
90 | }
91 |
92 | function sumAll() {
93 | var num = 1000;
94 | var total = sumNumbers(0, 0) + num;
95 | return total;
96 | }
97 |
98 | function fetchData() {
99 | var data = {};
100 | $.get('domain.com/products/1').then(function(response) {
101 | data = response.data;
102 | return data;
103 | });
104 | }
105 | ```
106 |
107 | 위와 같은 함수 설계에 관심을 갖다 보면 자연스럽게 함수형 프로그래밍에 대해 관심을 갖게 됩니다. 함수형 프로그래밍을 하기 위해서는 클로져(closure)라는 개념을 정확히 이해해야 하기 때문에 클로져 챕터에서 간략히 소개하겠습니다.
108 |
--------------------------------------------------------------------------------
/docs/js/number.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Number
3 | ---
4 |
5 | # 숫자(Number)
6 |
--------------------------------------------------------------------------------
/docs/js/object.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Object
3 | ---
4 |
5 | # Object
6 |
7 | 자바스크립트는 객체 기반 언어입니다. 객체는 키(key) - 값(value) 형태로 이루어져 있으며 아래와 같은 형태를 갖습니다.
8 |
9 | ```js
10 | var obj = {
11 | // 객체 내용
12 | };
13 | ```
14 |
15 | 위 코드는 obj라는 변수에 객체를 새로 할당한 코드입니다. 여기서 `{}`라는 기호가 객체를 의미하며 이를 객체 리터럴이라고 부릅니다. 일반적으로 객체를 생성할 때는 객체 리터럴을 사용하여 위와 같은 방식으로 선언합니다.
16 |
17 | ## 속성 추가
18 |
19 | 객체를 생성하고 나면 아래와 같은 방식으로 속성(property)를 추가할 수 있습니다.
20 |
21 | ```js
22 | // 객체 정의
23 | var obj = {};
24 |
25 | // num 속성을 추가하고 숫자 10을 할당
26 | obj.num = 10;
27 | ```
28 |
29 | 위와 같은 방법 이외에도 아래와 같이 속성을 추가할 수 있습니다.
30 |
31 | ```js
32 | // 객체 정의
33 | var obj = {};
34 |
35 | // num 속성을 추가하고 숫자 20을 할당
36 | obj['num'] = 20;
37 | ```
38 |
39 | ## 속성 값 변경
40 |
41 | 이미 정의한 속성을 변경하는 방법은 해당 속성을 다시 접근하여 값을 할당하는 것입니다.
42 |
43 | ```js
44 | // 객체 정의
45 | var obj = {};
46 |
47 | // num 속성을 추가하고 숫자 10을 할당
48 | obj.num = 10;
49 |
50 | // num 속성의 값에 숫자 20을 다시 할당
51 | obj.num = 20;
52 | ```
53 |
--------------------------------------------------------------------------------
/docs/js/operator.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Operator
3 | ---
4 |
5 | # 연산자(Operator)
6 |
7 | 연산자는 프로그래밍 로직을 구현할 때 논리식이나 산술식을 표현하기 위해 사용하는 기호들을 의미합니다. 주로 코드에서 흔하게 마주치는 아래와 같은 기호들을 의미합니다.
8 |
9 | - 산술 연산자 : `+`, `-`, `*`, `/`, `%`
10 | - 논리 연산자 : `||`, `&&`
11 | - 조건 연산자 : `? :`
12 | - 관계 연산자 : `>`, `<`, `===`
13 |
14 | ::: tip
15 | 각 연산자의 의미에 대해서는 MDN 문서 또는 자바스크립트 기초 책을 참고하세요.
16 | :::
17 |
18 | ## 연산자의 활용 1 - 변수 초기화
19 |
20 | 논리 연산자를 이용해 변수 초기화 하는 방법에 대해서 알아보겠습니다. 일반적으로 변수의 초기화는 다음과 같은 방법으로 많이 합니다.
21 |
22 | ```js {3-5}
23 | function fetchData(data) {
24 | var receivedData;
25 | if (data === undefined) {
26 | receivedData = localStorage.getItem('item');
27 | }
28 | }
29 | ```
30 |
31 | 함수의 인자로 넘어온 값을 if 문으로 확인한 뒤 추가 로직을 실행하는게 대부분의 초기화 문법입니다. 여기서 논리 연산자를 활용하면 아래와 같이 깔끔하게 코드를 구현할 수 있습니다.
32 |
33 | ```js {3}
34 | function fetchData(data) {
35 | var receivedData;
36 | receivedData = data || localStorage.getItem('item');
37 | }
38 | ```
39 |
40 | ## 연산자의 활용 2 - 조건문 대신 삼항 연산자
41 |
42 | 로직을 구현하다가 보면 if 문을 중첩해서 활용하는 경우가 많습니다. 아래와 같이 말이죠.
43 |
44 | ```js {3-7}
45 | if (data !== undefined) {
46 | num = 50;
47 | if (num > 10) {
48 | num = 100;
49 | } else {
50 | num = 0;
51 | }
52 | }
53 | ```
54 |
55 | 이럴 때 if 문 대신 삼항 연산자를 활용하면 더 코드를 간결하게 짤 수 있습니다.
56 |
57 | ```js {3}
58 | if (data !== undefined) {
59 | num = 50;
60 | num = num > 10 ? 100 : 0;
61 | }
62 | ```
--------------------------------------------------------------------------------
/docs/js/scope.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Scope
3 | ---
4 |
5 | # 스코프(Scope)
6 |
7 | 스코프란 변수의 유효 범위를 의미합니다.
8 |
9 | ## 글로벌 스코프
10 |
11 | 다른 프로그래밍 언어와는 다르게 자바스크립트의 변수는 유효 범위가 전역으로 시작합니다. 예를 들어 아래와 같은 변수 선언은 자바스크립트로 접근할 수 있는 모든 영역에서 같은 값을 갖습니다.
12 |
13 | ```js
14 | var a = 10;
15 | ```
16 |
17 | 만약 함수를 만들어 아래와 같이 접근하더라도 동일한 값을 출력하게 됩니다.
18 |
19 | ```js
20 | var a = 10;
21 |
22 | function getA() {
23 | console.log(a);
24 | }
25 |
26 | getA(); // 10
27 | ```
28 |
29 | ## 로컬 스코프
30 |
31 | 기본적으로 변수의 유효 범위는 전역 범위를 갖는다고 하지만, 함수 안에서 새로 선언하는 경우 함수 단위의 지역 범위인 함수 스코프를 갖습니다. 아래 코드를 보겠습니다.
32 |
33 | ```js
34 | var a = 10;
35 |
36 | function getA() {
37 | var a = 20;
38 | console.log(a);
39 | }
40 |
41 | getA(); // 20
42 | console.log(a); // 10
43 | ```
44 |
45 | 위 코드는 함수 바깥에서 변수 `a`를 선언하고 10을 대입한 뒤, `getA()`라는 함수를 선언하면서 함수 안에 변수 `a`를 새로 선언하고 20을 대입한 코드입니다. `getA()` 함수를 실행하면 함수 안의 변수인 `a`가 20의 값으로 콘솔에 출력됩니다. 함수의 실행이 끝나고 나서 `console.log(a);`로 다시 `a`의 값을 출력하면 10이 출력됩니다.
46 |
47 | 여기서 변수의 유효 범위는 함수 단위로 한정된다는 것을 알 수 있습니다.
48 |
49 | ## 스코프 체인
50 |
51 | 변수를 찾을 때 먼저 자신이 속한 스코프에서 찾고 없으면 상위 스코프에서 찾는 현상을 스코프 체인(Scope Chain)이라고 합니다.
52 |
53 | 간단한 예제로 앞의 코드를 다시 살펴보겠습니다.
54 |
55 | ```js
56 | // 글로벌 스코프
57 | var a = 10;
58 |
59 | function getA() {
60 | // 로컬 스코프
61 | console.log(a);
62 | }
63 |
64 | getA(); // 10
65 | ```
66 |
67 | `getA()` 함수가 실행이 되면 먼저 지역 범위인 `getA()` 함수 안에서 변수 `a`를 찾고 `getA()` 함수에는 변수 `a`가 없기 때문에 상위 스코프인 전역 범위에서 변수 `a`를 다시 찾습니다.
68 |
69 | 다음 예제를 보겠습니다.
70 |
71 | ```js
72 | // 글로벌 스코프
73 | var a = 10;
74 |
75 | function outer() {
76 | // 외부 함수 스코프
77 | var b = 20;
78 |
79 | function inner() {
80 | // 로컬 함수 스코프
81 | var c = 30;
82 | console.log(a);
83 | console.log(b);
84 | console.log(c);
85 | }
86 | inner();
87 | };
88 | outer();
89 |
90 | // 결과
91 | // 10
92 | // 20
93 | // 30
94 | ```
95 |
96 | 위의 코드는 `inner()` 함수에서 `a, b, c` 변수를 참조할 때 스코프 체인에 의해서 `inner()` 로컬 함수 스코프, `outer()` 외부 함수 스코프, 글로벌 스코프의 순서로 변수를 찾습니다.
97 |
98 | ## 렉시컬 스코프
99 |
100 | 앞에서 자신의 스코프에서 변수를 찾고 없으면 상위 스코프에서 찾는 스코프 체인에 대해 알아보았습니다. 그렇다면 이 상위 스코프는 어떤 기준으로 정하게 되는 것일까요?
101 |
102 | 자바스크립트는 함수를 어디서 선언하였는지에 따라서 상위 스코프를 결정하는 렉시컬 스코프(Lexical Scope) 규칙을 따릅니다.
103 |
104 | ```js
105 | // 글로벌 스코프
106 | var a = 10;
107 | var b = 20;
108 | function getA() {
109 | var b = a;
110 | getB();
111 | }
112 |
113 | function getB() {
114 | // 로컬 함수 스코프
115 | console.log(b);
116 | }
117 |
118 | getB(); // 20
119 | getA(); // 20
120 |
121 | ```
122 |
123 | 위 코드에서 `getB()` 함수는 전역 범위에 선언되었기 때문에 호출된 위치와 상관없이 상위 스코프로 갖는 글로벌 스코프와 자기 자신인 로컬 함수 스코프를 최종 스코프를 갖게 되서 둘 다 20을 출력합니다.
124 |
125 | 이와 반대되는 개념인 다이나믹 스코프(Dynamic Scope) 규칙을 따르는 언어는 함수의 호출 위치에 따라 상위 스코프가 결정됩니다.
126 |
127 | ```sh
128 | #!/bin/sh
129 |
130 | A=10
131 | B=20
132 |
133 | getA(){
134 | B=$A
135 | getB
136 | }
137 |
138 | getB(){
139 | echo $B
140 | }
141 |
142 | getB # 20
143 | getA # 10
144 | ```
145 |
146 | 다이나믹 스코프 규칙을 따르는 쉘 스크립트는 20, 10이 출력 됩니다.
147 |
--------------------------------------------------------------------------------
/docs/js/string.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: String
3 | ---
4 |
5 | # 문자열(String)
6 |
7 | 문자열은 변수의 여러 타입 중 하나입니다. 일반적으로 스트링이라고도 부르며 아래와 같이 선언합니다.
8 |
9 | ```js
10 | var a = 'hello';
11 | ```
12 |
13 | 위 코드는 `a`라는 변수에 `hello`라는 문자열을 할당한 코드입니다. 문자열은 이와 같이 작은 따옴표(') 또는 큰 따옴표(")를 이용하여 정의할 수 있습니다.
14 |
15 | ## 숫자와의 구분
16 |
17 | 자바스크립트는 코드를 실행하는 시점에 변수의 타입을 결정하는 언어입니다. 이러한 특징이 초심자들에게는 편하지만 아래와 같이 타입을 헷갈리게 하는 경우가 생깁니다.
18 |
19 | ```js
20 | var a = 10;
21 | var b = '10';
22 | console.log(a); // 10
23 | console.log(b); // 10
24 | ```
25 |
26 | 위 코드에서 변수 `a`는 숫자고 변수 `b`는 문자열입니다. 콘솔 로그 코드를 이용하여 콘솔에 변수의 값을 출력해보면 육안으로는 구분하기가 어렵다는 사실을 알 수 있습니다.
27 |
28 | 여기서 `typeof`를 이용하여 변수의 타입을 확인할 수도 있겠지만 실무 관점에서는 아래와 같은 방법으로 구분할 수 있습니다.
29 |
30 | ```js
31 | console.log(a.length); // undefined
32 | console.log(b.length); // 2
33 | ```
34 |
35 | 여기서 `length` 라는 예약어는 자바스크립트에서 변수의 타입에 따라 제공하는 기능입니다. length는 문자열, 배열의 길이를 숫자 형태로 확인할 수 있습니다. 따라서 `b`는 문자열이고 `a`는 숫자임을 추측할 수가 있습니다.
36 |
37 | 위와 같이 타입에 따라 기본적으로 제공되는 예약어 및 기능들을 자바스크립트 내장 함수(JavaScript Built-in API / JavaScript Native API)라고 합니다. 내장 함수에 대해서는 [prototype](/js/prototype.html)에서 자세히 설명하겠습니다.
38 |
--------------------------------------------------------------------------------
/docs/js/this.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: this
3 | ---
4 |
5 | # this
6 |
7 | this는 함수의 실행 컨텍스트를 가리키는 예약어입니다. 여기서 실행 컨텍스트는 사전적인 정의로 '**함수가 실행되는 환경**'이며 좀 더 쉽게 접근하기 위해서는 '**함수가 실행될 때의 컨텍스트**'로 이해하시는 게 좋겠습니다.
8 |
9 | 다른 언어와 다르게 자바스크립트의 this는 상황에 따라 다른 값들을 가르킵니다. 이 때문에 자바스크립트가 조금 어렵게 느껴지는 부분도 있습니다.
10 |
11 | 여기서는 this가 가장 많이 활용되는 상황들에 대해서 알아보겠습니다.
12 |
13 | ## 첫 번째 this
14 |
15 | 아래의 코드를 브라우저 콘솔에서 실행하면 어떻게 될까요?
16 |
17 | ```js
18 | console.log(this); // window
19 | ```
20 |
21 | this의 가장 기본적인 컨텍스트는 **글로벌(전역) 컨텍스트**입니다. 여기서 출력된 window는 자바스크립트의 최상위 객체를 가리킵니다.
22 |
23 | ## 두 번째 this
24 |
25 | 아래와 같은 객체가 있다고 가정하겠습니다.
26 |
27 | ```js
28 | var obj = {
29 | num: 10,
30 | printNum: function() {
31 | console.log(this.num);
32 | }
33 | };
34 | ```
35 |
36 | 여기서 `obj.printNum()`을 실행하면 어떻게 될까요?
37 |
38 | ```js
39 | obj.printNum(); // 10
40 | ```
41 |
42 | **객체 속성 함수 안에서의 this는 기본적으로 해당 객체를 가리킵니다.**
43 |
44 | ## 세 번째 this
45 |
46 | 세 번째로 살펴볼 this는 먼저 [함수 선언문](#함수-선언문), [생성자 함수](#생성자-함수)를 알아야 합니다. 혹시 아직 개념이 낯선 분들은 링크를 클릭해서 간단하게 살펴보시고 계속 읽어나가시는 것을 추천드립니다.
47 |
48 | 자 그럼 일반 함수(함수 선언문)에서의 this는 어떤 것을 가리킬까요?
49 |
50 | ```js
51 | function showComment() {
52 | console.log(this);
53 | }
54 | ```
55 |
56 | 위 함수를 아래와 같이 실행시키면 `window` 객체를 가리키고 있다는 것을 알 수 있습니다. 결론적으로 **일반 함수의 this는 전역 컨텍스트입니다.**
57 |
58 | ```js
59 | showComment(); // window
60 | ```
61 |
62 | 그럼 이번엔 생성자 함수의 this를 확인해보겠습니다.
63 |
64 | ```js
65 | function Developer() {
66 | console.log(this);
67 | }
68 | var dev = new Developer();
69 | ```
70 |
71 | 위 코드는 실행하자마자 바로 아래와 같은 결과를 콘솔에 출력합니다.
72 |
73 | ```js
74 | Developer {}
75 | ```
76 |
77 | 그 이유는 `new`로 인스턴스를 생성하는 순간 함수가 실행되기 때문입니다. 그리고 여기서 알 수 있는 사실은 **생성자 함수의 this는 함수의 내부를 가리킨다는 것**입니다.
78 |
79 | ## 네 번째 this
80 |
81 | 네 번째로 살펴볼 this는 실제로 웹 개발을 할 때 가장 많이 마주하게 되는 this입니다. 바로 데이터를 받아올 때 사용하는 HTTP 요청과 같은 비동기 처리 코드입니다.
82 |
83 | ```js
84 | function fetchData() {
85 | axios.get('domain.com/products').then(function() {
86 | console.log(this);
87 | });
88 | }
89 | ```
90 |
91 | 위 함수를 실행하면 어떤 결과가 나타날까요? 정답은 window입니다.
92 |
93 | ```js
94 | fetchData(); // window
95 | ```
96 |
97 | 기본적으로 HTTP 요청과 같은 비동기 처리 코드는 전역 컨텍스트를 갖습니다. 정리해서 **비동기 처리 코드의 콜백 함수는 전역 컨텍스트를 가리킨다**고 보면 되겠습니다.
98 |
99 | ## 부록
100 |
101 | 위 본문을 이해하는데 도움이 되는 자료들입니다.
102 |
103 | ### 함수 선언문
104 |
105 | 함수 선언문은 아래와 같은 함수 정의 방식을 의미합니다.
106 |
107 | ```js
108 | // 함수 선언문
109 | function functionName() {
110 | // ...
111 | }
112 | ```
113 |
114 | ### 생성자 함수
115 |
116 | 그리고 생성자 함수는 아래와 같이 함수를 이용해 새 인스턴스를 선언하는 함수를 의미합니다.
117 |
118 | ```js
119 | function Developer() {
120 | // ...
121 | }
122 | var dev = new Developer();
123 | ```
124 |
125 | ::: tip
126 | 자바스크립트는 프로토타입 기반 언어입니다. 클래스 기반 언어가 아니기 때문에 위와 같이 함수를 이용하여 인스턴스를 생성할 수 있습니다. 자바 개발자 분들은 클래스가 없다고 당황하지 마세요 :)
127 | :::
128 |
--------------------------------------------------------------------------------
/docs/js/variable.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Variable
3 | ---
4 |
5 | # 변수(Variable)
6 |
7 | ## 소개
8 |
9 | 변수는 프로그래밍 언어의 가장 기본적인 개념입니다. 변수는 변하는 값을 의미하고 특정 데이터를 담는 용기나 그릇과 같습니다. 변하는 값을 저장하는 공간을 변수라고 합니다. 프로그래밍을 할 때 로직의 대부분은 변수를 기반으로 작성하게 됩니다. 단순한 덧셈 등의 사칙 연산부터 시작하여 복잡한 로직의 계산까지 모두 변수가 관여합니다.
10 |
11 | 자바스크립트에서 변수는 아래와 같이 정의할 수 있습니다.
12 |
13 | ```js
14 | var a;
15 | ```
16 |
17 | 위는 `a`라는 변수를 선언한 코드입니다. 변수를 선언하고 아무 값도 할당하지 않으면 기본적으로 `undefined` 라는 초기값을 가집니다.
18 |
19 | ## 변수에 값 할당하기
20 |
21 | 일반적으로 변수를 선언하고 나면 변수 안에 원하는 값을 정의할 수 있습니다.
22 |
23 | ```js
24 | var a;
25 | a = 10;
26 | ```
27 |
28 | 위 코드는 `a`라는 변수를 정의하고 이 변수에 `10`이라는 숫자를 할당한 코드입니다. 다른 프로그래밍 언어와 달리 자바스크립트가 입문자에게 좋은 이유는 위와 같이 변수의 타입(종류)을 정의하지 않아도 코드를 실행할 때 자동으로 타입이 결정된다는 점입니다.
29 |
30 | 아래와 같이 유연하게 코드의 값을 바꿀 수 있습니다.
31 |
32 | ```js
33 | var a;
34 | a = 10;
35 | console.log(a); // 10
36 |
37 | a = 'hi';
38 | console.log(a); // hi
39 |
40 | a = false;
41 | console.log(a); // false
42 | ```
43 |
44 | ## 변수명 규칙
45 |
46 | 변수명은 영어와 한글 모두 사용할 수 있습니다. 숫자도 사용할 수는 있지만 숫자로 시작되어선 안됩니다. `$`, `_` 등의 특수문자를 사용할 수 있고 대소문자가 구분됩니다.
47 |
48 | ```js
49 | // 가능한 변수명
50 | var myName;
51 | var 내이름;
52 | var 내첫번째이름;
53 | var _a;
54 | var $won;
55 | ```
56 |
57 | 변수명에 [예약어](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_keywords_as_of_ecmascript_2015)를 사용할 수 없습니다.
58 |
59 | ```js
60 | // 불가능한 변수명
61 | var 1이름;
62 | var continue;
63 | var function;
64 | ```
65 |
66 | ## 변수의 타입 확인하기 - typeof
67 |
68 | 앞에서 본 것처럼 `a`라는 변수의 값은 숫자, 문자열, 진위 값 순서로 변경되었습니다. 여기서는 다행히 할당한 값을 쉽게 구분할 수 있었지만 실제로 코딩하다보면 아래와 같이 쉽게 타입을 추측하기가 어렵습니다.
69 |
70 | ```html
71 |
72 | ```
73 |
74 | ```js
75 | var divElement = document.querySelector('input').value;
76 | console.log(divElement);
77 | ```
78 |
79 | 이 때 `divElement` 변수의 타입을 확인할 수 있는 방법은 아래와 같습니다.
80 |
81 | ```js
82 | console.log(typeof divElement);
83 | ```
84 |
85 | 이처럼 변수의 타입은 typeof 라는 예약어로 확인할 수 있습니다.
86 |
87 | ::: tip
88 | 예약어 : 언어에서 미리 지정해놓은 키워드, 단어
89 | :::
90 |
--------------------------------------------------------------------------------
/docs/legacy/chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Wrapping External Library II
3 | ---
4 |
5 | # 차트 라이브러리 통합
6 |
7 | 외부 라이브러리를 뷰에 통합하는 실습 2번째는 차트 라이브러리입니다. 차트 라이브러리는 기업의 백오피스(Back office)나 모니터링 시스템에서 흔하게 사용됩니다. 뷰 프레임워크 용으로도 차트 라이브러리들이 나오고 있지만 아직은 뷰와 통합된 라이브러리들이 많지가 않아 직접 통합해서 사용할 줄 알아야 합니다.
8 |
9 | ## 실습 예제
10 |
11 | 통합할 차트 라이브러리는 [Chart.js](https://www.chartjs.org/)입니다. 이미 뷰로 통합된 라이브러리가 오픈소스로 나와 있지만 직접 통합해보면서 감각을 익혀보겠습니다. 그리고 이번에는 뷰 플러그인으로 변환하여 사용하는 방법들을 알아보겠습니다.
12 |
13 | ## 실습 포인트
14 |
15 | 1. 컴포넌트 모듈화
16 | 2. 뷰 라이프사이클에 대한 이해
17 | 3. 컴포넌트 통신 방식에 대한 이해
18 | 4. 플러그인에 대한 이해
19 |
20 | ## 실습 코드
21 |
22 | - [연습 코드는 여기서](https://github.com/joshua1988/vue-camp/tree/vue6-class/2_todo/chart-with-plugin/exercise)
23 | - [답안 코드는 여기서](https://github.com/joshua1988/vue-camp/tree/vue6-class/2_todo/chart-with-plugin/answer)
--------------------------------------------------------------------------------
/docs/legacy/datepicker.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Wrapping External Library I
3 | ---
4 |
5 | # 날짜 선택기 라이브러리 통합
6 |
7 | 실무의 레거시 프로젝트를 뷰로 바꾸는 경우 기존에 사용하던 라이브러리를 뷰로 래핑해야 하는 경우가 생깁니다. 일반적으로 기존 시스템에 적용된 라이브러리는 바닐라 자바스크립트 또는 jQuery 기반으로 되어 있는 경우가 많습니다. 따라서, 뷰 개발자는 이와 같은 라이브러리를 뷰의 동작 원리에 맞춰 통합할 줄 알아야 합니다.
8 |
9 | 라이브러리를 뷰로 래핑할 때 알아야 하는 뷰의 특징은 가상 돔(Virtual DOM) 기반으로 템플릿을 구현한다는 점입니다. 뷰 인스턴스가 화면에 부착되는 시점이 언제인지 잘 모르시는 분들은 [뷰 인스턴스 라이프사이클](../vue/life-cycle.html)을 다시 살펴보세요.
10 |
11 | ## 실습 예제
12 |
13 | 뷰로 통합할 라이브러리는 날짜 선택기(DatePicker) 라이브러리입니다. 날짜 선택기 라이브러리는 jQuery 플러그인으로도 있고 독립 라이브러리로도 많이 있습니다. 여기서는 [Pickaday](https://github.com/dbushell/Pikaday)를 기준으로 실습하겠습니다.
14 |
15 | ## 실습 포인트
16 |
17 | 1. 컴포넌트 모듈화
18 | 2. 뷰 라이프사이클에 대한 이해
19 | 3. 컴포넌트 통신 방식에 대한 이해
20 | 4. v-model 내부 동작 방식에 대한 이해
21 |
22 | ## 실습 코드
23 |
24 | - [연습 코드는 여기서](https://github.com/joshua1988/vue-camp/tree/vue6-class/2_todo/external-library/exercise)
25 | - [답안 코드는 여기서](https://github.com/joshua1988/vue-camp/tree/vue6-class/2_todo/external-library/answer)
26 |
--------------------------------------------------------------------------------
/docs/legacy/form.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Form
3 | ---
4 |
5 | # Form
6 |
7 | 앞에서 연습했던 제이쿼리의 사고 방식에서 벗어나기 위해 실습 한 개를 더 해보겠습니다. 제이쿼리로 구현된 사용자 입력폼이 아래와 같이 있을 때 어떻게 뷰로 바꿀 수 있을까요?
8 |
9 | ```html
10 |
36 |
37 |
67 | ```
68 |
69 | [연습 코드는 여기서](https://github.com/joshua1988/vue-camp/tree/vue6-class/1_essentials/form/exercise)
70 | [답안 코드는 여기서](https://github.com/joshua1988/vue-camp/tree/vue6-class/1_essentials/form/answer)
--------------------------------------------------------------------------------
/docs/legacy/jquery-to-vue.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: jQuery to Vue
3 | ---
4 |
5 | # 제이쿼리에서 뷰 사고 방식으로 전환하기
6 |
7 | ## 뷰 개발자가 마주하는 현실
8 |
9 | 제이쿼리(jQuery)는 현재까지도 많은 기업들이 사용하고 있는 자바스크립트 라이브러리입니다. 아마도 뷰를 배우고 나면 가장 먼저 부딪히는 일들이 제이쿼리로 제작된 애플리케이션의 기능을 수정해야 하는 일 일겁니다.
10 |
11 | ::: tip
12 | jQuery는 DOM을 쉽게 조작하기 위한 라이브러리입니다.
13 | :::
14 |
15 | ## 제이쿼리와 뷰의 차이점
16 |
17 | 제이쿼리와 뷰의 가장 큰 차이점은 화면 요소를 직접 접근하느냐 그렇지 않느냐입니다. 더 쉽게 이해하기 위해 두 라이브러리의 DOM 접근 방법을 살펴보겠습니다. HTML 코드는 아래와 같이 간단한 버튼 태그를 사용하겠습니다.
18 |
19 | ```html
20 |
21 | ```
22 |
23 | ### 제이쿼리의 버튼 태그 접근
24 |
25 | 제이쿼리는 특정 태그를 접근하기 일반적으로 태그에 id 속성을 부여하고 제이쿼리 선택자($)를 활용합니다.
26 |
27 | ```html
28 |
29 | ```
30 |
31 | ```js
32 | $('#btn');
33 | ```
34 |
35 | 위 코드는 btn 아이디를 가지는 DOM을 접근하는 코드입니다.
36 |
37 | ### 뷰의 버튼 태그 접근
38 |
39 | 이번엔 뷰입니다. 뷰로 특정 태그로 접근하려면 ref 속성을 활용합니다. 위의 버튼 태그에 ref 속성을 부여하고 접근해보겠습니다.
40 |
41 | ```html
42 |
43 | ```
44 |
45 | ```js
46 | this.$refs.btn
47 | ```
48 |
49 | 이처럼 바로 접근하고 싶은 태그에 ref 속성을 지정하여 사용합니다.
50 |
51 | ## 제이쿼리와 뷰를 같이 쓸 때 주의할 점
52 |
53 | 보통 뷰를 배우시고 나면 기존의 제이쿼리 기반 시스템을 개편하거나 새로 서비스를 만드는 경우가 많습니다. 여기서 전자의 비율이 상당히 높습니다. 그러면 제이쿼리 기반의 코드, 플러그인 라이브러리 사용시에 주의할 점을 알아보겠습니다. 주의할 점은 바로 뷰의 인스턴스 라이프사이클입니다.
54 |
55 | 뷰의 라이프사이클이란 뷰의 인스턴스가 생성되고 소멸되기까지의 과정입니다. 그리고 라이프사이클 훅이라는 게 있는데 주로 사용되는 건 아래와 같습니다.
56 |
57 | - created
58 | - beforeMount
59 | - mounted
60 |
61 | 여기서 제이쿼리의 선택자로 HTML 태그를 접근할 수 있는 시점은 mounted 단계입니다. 코드로 예를 들어보겠습니다.
62 |
63 | ```html
64 |
65 |
66 |
67 |
68 |
69 |
70 |
81 | ```
82 |
83 | 위 코드는 뷰의 싱글 파일 컴포넌트(vue 파일 확장자) 코드 구조에서 제이쿼리를 함께 사용하는 모습입니다. `` 안에 HTML 태그가 있으니 제이쿼리로 항상 접근할 수 있을 것 같지만 실제로는 그렇지 않습니다. 왜냐하면 ``은 실제 돔으로 변환되기 전의 Virtual DOM 상태이기 때문에 적절한 라이프사이클 훅에서 접근하지 않으면 접근할 수 없기 때문입니다.
84 |
85 | 예를 들어, 위의 코드는 버튼을 클릭했을 때 hi라는 경고창을 띄우지만 아래의 코드는 아무런 반응이 없습니다.
86 |
87 | ```html
88 |
89 |
90 |
91 |
92 |
93 |
94 |
105 | ```
--------------------------------------------------------------------------------
/docs/nuxt/automatic-routing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Automatic Routing
3 | ---
4 |
5 | # Nuxt의 페이지 라우팅
6 |
7 | 뷰 싱글 페이지 애플리케이션에서는 라우터 설정을 아래와 같이 일일이 해줘야 하는데 반해 넉스트에서는 폴더와 파일 기반으로 라우터를 자동으로 생성해 줍니다.
8 |
9 | ```js
10 | // 뷰 싱글 페이지 애플리케이션의 라우터 설정 파일
11 | new VueRouter({
12 | routes: [
13 | {
14 | path: 'URL 주소',
15 | component: URL 주소에 접근했을 때 표시할 페이지 컴포넌트
16 | }
17 | ],
18 | });
19 | ```
20 |
21 | ## 페이지 기반 라우터 자동 생성
22 |
23 | 넉스트에서 어떻게 파일과 폴더 기반으로 라우팅 할 수 있는지 살펴보겠습니다. 넉스트 프로젝트를 생성하면 `pages` 폴더가 존재하는데 그 아래에 다음과 같이 파일을 생성합니다.
24 |
25 | 
26 |
27 | 위와 같이 파일을 생성하면 URL을 각각 `/`, `/login`, `/main`으로 접근했을 때 `index.vue`, `login.vue`, `main.vue` 컴포넌트가 화면에 표시됩니다. 이때 레이아웃 컴포넌트에 ``라는 태그가 있어야 합니다.
28 |
29 | ```html{4}
30 |
31 |
32 |
33 |
34 |
35 |
36 | ```
37 |
38 | 여기서 일일이 URL을 입력하여 들어가기엔 번거로우므로 아래와 같이 페이지 이동 링크인 `` 를 추가합니다.
39 |
40 | ```html{5-7}
41 |
42 |
43 |
44 |
45 | 시작
46 | 로그인
47 | 메인
48 |
49 |
50 |
51 |
52 | ```
53 |
54 | 이제 각 링크를 클릭하면 아래와 같이 동작합니다.
55 |
56 | 
57 |
58 | ## 뷰 라우터와의 비교
59 |
60 | 위에서 살펴본 넉스트의 라우팅 방식을 뷰 라우터 방식과 비교하면 아래와 같습니다.
61 |
62 | | Nuxt.js | VueRouter |
63 | |-------|---------|
64 | | `` | `` |
65 | | `` | `` |
66 |
67 | 이처럼 넉스트는 라우터 설정 파일을 일일이 생성 및 설정하지 않아도 되어 편리합니다.
68 |
69 | :::tip
70 | ``, `` 모두 뷰 컴포넌트입니다. 따라서 ``와 ``와 같습니다. ``도 그럼 동일한 케밥 네이밍으로 변환하면 ``로도 쓸 수 있겠죠? :)
71 | :::
--------------------------------------------------------------------------------
/docs/nuxt/deployment.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Nuxt Deployment
3 | ---
4 |
5 | # Nuxt 서비스 배포 방법
6 |
7 | Nuxt.js 서비스를 배포하는 방법은 프로젝트를 생성할 때 선택한 Universal 모드의 배포 방식에 따라 다릅니다. 아래 2가지 유형에 따른 배포 방법을 살펴보겠습니다.
8 |
9 | 
10 |
11 | ## SSR(Server Side Rendering)
12 |
13 | SSR 모드는 위 그림에서 Deployment Target을 `Server(Node.js hosting)`으로 선택한 경우입니다. 넉스트 설정 파일의 `target` 속성 값이 아래와 같이 `server`로 지정됩니다.
14 |
15 | ```js
16 | // nuxt.config.js
17 | export default {
18 | target: 'server'
19 | }
20 | ```
21 |
22 | :::tip
23 | `target` 속성의 기본 값은 `server`이기 때문에 설정 파일에 보이지 않더라도 괜찮습니다 :)
24 | :::
25 |
26 | SSR 모드로 생성한 웹 서비스는 배포하려는 서버에 Node.js 서버를 실행할 수 있는 형태로 배포해야 합니다. 각 클라우드 플랫폼별 자세한 가이드는 아래 링크를 참고하세요.
27 |
28 | - [Azure](https://nuxtjs.org/docs/2.x/deployment/deployment-azure-portal)
29 | - [Google App Engine](https://nuxtjs.org/docs/2.x/deployment/appengine-deployment)
30 | - [Heroku](https://nuxtjs.org/docs/2.x/deployment/heroku-deployment)
31 |
32 | ## SSG(Static Site Generation)
33 |
34 | SSG 모드는 Deployment Target을 `Static(Static/Jamstack hosting)`을 선택한 경우입니다. 넉스트 설정 파일의 `target` 속성 값은 `static`으로 지정됩니다.
35 |
36 | ```js
37 | // nuxt.config.js
38 | export default {
39 | target: 'static'
40 | }
41 | ```
42 |
43 | SSG 모드는 사용자의 페이지 URL 요청이 들어올 때마다 서버에서 그려서 브라우저에 보내주는 SSR 모드와 다르게 웹 서비스를 구성하는 모든 페이지를 미리 그려야 하기 때문에 스태틱 서버에 배포하는 형태로 진행해야 합니다. Netlify 등의 CD(Continuous Delivery) 플랫폼을 이용하여 손쉽게 배포할 수 있습니다.
44 |
45 | - [Netlify](https://nuxtjs.org/docs/2.x/deployment/netlify-deployment)
46 | - [AWS](https://nuxtjs.org/docs/2.x/deployment/deployment-amazon-web-services)
47 | - [Azure Static Web Apps](https://nuxtjs.org/docs/2.x/deployment/deployment-azure-static-web-apps)
48 | - [Github](https://nuxtjs.org/docs/2.x/deployment/github-pages)
49 |
50 | :::tip
51 | JAMStack이란 JavaScript & API & Markup을 의미하며 API 서버 없이 사이트를 제작하는 방식을 의미합니다.
52 | :::
--------------------------------------------------------------------------------
/docs/nuxt/folder-structure.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Nuxt Folder Structure
3 | ---
4 |
5 | # Nuxt의 폴더 구조
6 |
7 | 넉스트의 프로젝트 생성 명령어를 실행하고 난 뒤 생성된 넉스트의 기본 폴더 구조를 살펴보겠습니다.
8 |
9 | ## Nuxt의 폴더 구조
10 |
11 | 넉스트의 기본 폴더 구조입니다.
12 |
13 | 
14 |
15 | - `.nuxt` : 넉스트 빌드 결과물 폴더
16 | - `assets` : 스타일 시트, 이미지, 폰트 등 웹 리소스 폴더입니다.
17 | - `components` : 뷰 컴포넌트 폴더입니다.
18 | - `layouts` : 레이아웃 컴포넌트 폴더입니다. 레이아웃 패턴에 대해서는 [레이아웃 챕터](./layouts.md)에서 알아보겠습니다.
19 | - `middleware` : 미들웨어(페이지를 화면에 표시하기 전에 실행할 수 있는 함수) 파일 폴더입니다. 미들웨어에 대해서는 [미들웨어 챕터](./middleware.md)에서 알아보겠습니다.
20 | - `pages` : 특정 URL에 접근했을 때 표시될 페이지 컴포넌트 폴더입니다. 넉스트의 라우팅 방식은 [자동 라우팅 챕터](./automatic-routing.md)를 참고하세요.
21 | - `plugins` : 뷰 플러그인 폴더입니다. 뷰의 플러그인이 궁금하신 분들은 [뷰 플러그인 챕터](../reuse/plugins.md)를 참고하세요.
22 | - `static` : 빌드 했을 때 서버의 루트에 존재해야 하는 파일들의 폴더입니다. 파비콘이나 `robots.txt` 등의 파일이 위치합니다.
23 | - `store` : 뷰엑스 폴더입니다. 뷰엑스가 낯선 분들은 [상태 관리 챕터](../vuex/concept.md)를 참고하세요.
--------------------------------------------------------------------------------
/docs/nuxt/images/age-of-next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/age-of-next.png
--------------------------------------------------------------------------------
/docs/nuxt/images/app-mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/app-mode.png
--------------------------------------------------------------------------------
/docs/nuxt/images/axios-options.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/axios-options.png
--------------------------------------------------------------------------------
/docs/nuxt/images/comp-commu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/comp-commu.png
--------------------------------------------------------------------------------
/docs/nuxt/images/comp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/comp.png
--------------------------------------------------------------------------------
/docs/nuxt/images/csr-result.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/csr-result.gif
--------------------------------------------------------------------------------
/docs/nuxt/images/document-access-error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/document-access-error.png
--------------------------------------------------------------------------------
/docs/nuxt/images/fetch-page-navigation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/fetch-page-navigation.gif
--------------------------------------------------------------------------------
/docs/nuxt/images/fetch-ssr-rendering.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/fetch-ssr-rendering.gif
--------------------------------------------------------------------------------
/docs/nuxt/images/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/folder.png
--------------------------------------------------------------------------------
/docs/nuxt/images/install-axios.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/install-axios.png
--------------------------------------------------------------------------------
/docs/nuxt/images/nuxt-folder-structure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/nuxt-folder-structure.png
--------------------------------------------------------------------------------
/docs/nuxt/images/nuxt-js-operation-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/nuxt-js-operation-flow.png
--------------------------------------------------------------------------------
/docs/nuxt/images/nuxt-routing.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/nuxt-routing.gif
--------------------------------------------------------------------------------
/docs/nuxt/images/nuxt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/nuxt.png
--------------------------------------------------------------------------------
/docs/nuxt/images/og-tag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/og-tag.png
--------------------------------------------------------------------------------
/docs/nuxt/images/page-folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/page-folder.png
--------------------------------------------------------------------------------
/docs/nuxt/images/prefetching.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/prefetching.gif
--------------------------------------------------------------------------------
/docs/nuxt/images/rendering-mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/rendering-mode.png
--------------------------------------------------------------------------------
/docs/nuxt/images/ssr-vs-csr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/ssr-vs-csr.png
--------------------------------------------------------------------------------
/docs/nuxt/images/store-index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/nuxt/images/store-index.png
--------------------------------------------------------------------------------
/docs/nuxt/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | ---
4 |
5 | # Nuxt란?
6 |
7 | Nuxt는 Vue.js로 빠르게 웹을 제작할 수 있게 도와주는 프레임워크입니다. 웹 애플리케이션을 제작할 때 필요한 [뷰엑스](/vuex/concept.html), [라우터](/vue/router.html), [Axios](/vue/axios.html)와 같은 라이브러리들을 미리 구성하여 싱글 페이지 애플리케이션(Single Page Application), 서버 사이드 렌더링(Server Side Rendering), 정적 웹 사이트(Static Generated Website)를 쉽게 제작할 수 있습니다.
8 |
9 | ::: tip
10 | 서버 사이드 렌더링이란 웹 페이지의 내용을 서버에서 모두 작성해서 클라이언트(브라우저)로 보낸 뒤 화면에 그리는 방식을 의미합니다.
11 | :::
12 |
13 | ## Nuxt의 장점
14 |
15 | Nuxt로 개발했을 때의 장점은 다음과 같습니다.
16 |
17 | - 검색 엔진 최적화
18 | - 초기 프로젝트 설정 비용 감소와 생산성 향상
19 | - ESLint, Prettier
20 | - 라우터, 스토어 등의 라이브러리 설치 및 설정 파일 필요 X
21 | - 파일 기반의 라우팅 방식. 설정 파일 자동 생성
22 | - 페이지 로딩 속도와 사용자 경험 향상
23 | - 브라우저가 하는 일을 서버에 나눠준다
24 | - 모르면 지나칠 수 있는 코드 스플리팅이 기본으로 설정
25 |
26 | ## Nuxt 특징
27 |
28 | Nuxt의 특징을 요약해보면 크게 아래와 같이 구분됩니다.
29 |
30 |
31 |
38 |
39 | - [서버 사이드 렌더링](./ssr.md)
40 | - [규격화된 폴더 구조(layout, store, middleware, plugins 등)](./folder-structure.md)
41 | - [pages 폴더 기반의 자동 라우팅 설정](./automatic-routing.md)
42 | - [코드 스플리팅](../advanced/code-splitting.md)
43 | - 비동기 데이터 요청 속성
44 | - ES6/ES6+ 변환
45 | - 웹팩을 비롯한 기타 설정
46 |
47 | ## Nuxt 시작하기
48 |
49 | Nuxt 프로젝트를 구성하기 위해 다음 명령어를 실행합니다.
50 |
51 | ```bash
52 | npm init nuxt-app 프로젝트명
53 | ```
54 |
55 | 프로젝트가 생성되고 나면 명령어 실행 창을 참고하여 아래 명령어를 실행합니다.
56 |
57 | ```bash
58 | cd 프로젝트명
59 | npm run dev
60 | ```
61 |
62 | 명령어 실행 창에 안내된 주소를 브라우저에 입력하고 결과를 확인합니다.
63 |
64 | ## Nuxt를 쉽게 배울 수 있는 온라인 강의
65 |
66 | 글보다 더 쉽고 빠르게 배울 수 있는 온라인 강의를 안내합니다.
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/docs/nuxt/lifecycle.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Nuxt Lifecycle
3 | ---
4 |
5 |
6 | ## 참고
7 |
8 | - [Nuxt Lifecycle](https://ko.nuxtjs.org/docs/2.x/concepts/nuxt-lifecycle/)
9 | - [lifecyecle compare table](https://tech.onestopbeauty.online/front-end/understanding-nuxt-vue-hooks-and-lifecycle-part3/)
--------------------------------------------------------------------------------
/docs/nuxt/nuxt-config.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Config
3 | ---
4 |
5 | # Nuxt 설정 파일
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/nuxt3/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction 🆕
3 | ---
4 |
5 | # Nuxt 3란?
6 |
7 | [Vue Composition API](../reuse/composition) 기반으로 Nuxt를 쉽게 사용할 수 있도록 업그레이드 된 서버 사이드 렌더링 프레임워크입니다.
8 |
9 | ## Nuxt 2와의 차이점
10 |
11 | - Composition API 지원 강화
12 | - 타입스크립트 지원 강화
13 | - 기존의 주요 API가 모두 `useXX()` 패턴으로 변경
14 |
15 | ## 폴더 구조
16 |
17 | [기존 Nuxt 버전과 폴더 구조는 동일합니다.](https://joshua1988.github.io/vue-camp/nuxt/folder-structure.html#nuxt%E1%84%8B%E1%85%B4-%E1%84%91%E1%85%A9%E1%86%AF%E1%84%83%E1%85%A5-%E1%84%80%E1%85%AE%E1%84%8C%E1%85%A9-2)
18 |
19 | ## 데이터 호출 방식
20 |
21 | Nuxt 3의 데이터 호출 방식은 다음 문서를 참고하세요.
22 | [Nuxt 3 데이터 호출 방법](./data-fetching)
23 |
24 | ## 상태 관리
25 |
26 | Nuxt 3에서는 최신 Vuex 버전인 Pinia로 상태 관리를 합니다.
27 | []()
28 |
29 | ## 그외 주요 특징
30 |
31 | 그외에 Nuxt의 주요 특징은 Nuxt 2와 동일합니다.
32 | [Nuxt 주요 특징 7가지](../nuxt/intro#nuxt-특징)
--------------------------------------------------------------------------------
/docs/package-manager/npm-vs-yarn.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: NPM vs Yarn
3 | ---
4 |
5 | # npm과 yarn
6 |
7 | npm과 yarn은 자바스크립트 런타임 환경인 노드(Node.js)의 패키지 관리자입니다. 전 세계의 개발자들이 자바스크립트로 만든 다양한 패키지를 [npm 온라인 데이터베이스](https://www.npmjs.com/)에 올리면 npm, yarn과 같은 패키지 관리자를 통해 설치 및 삭제가 가능합니다. 그리고 명령 줄 인터페이스(Command-line interface, CLI)를 통해 패키지 설치 및 삭제뿐 아니라 패키지 버전 관리, 의존성 관리도 편리하게 할 수 있습니다. npm과 yarn의 정확한 차이는 무엇일까요? 우리는 어떤 툴을 사용해서 패키지 관리를 해야 할까요?
8 |
9 | ## npm
10 |
11 | npm은 노드 패키지 매니저(Node Package Manager)의 줄임말로 노드를 설치할 때 자동으로 설치되는 기본 패키지 관리자입니다. 크게 두 가지 역할을 수행합니다.
12 |
13 | 1. 첫 번째 역할은 [온라인 플랫폼](https://www.npmjs.com/)입니다. 사람들이 노드 패키지를 만들고, 업로드하고, 공유할 수 있는 공간으로 누구나 온라인 플랫폼(npm 레지스트리)에 게시된 패키지를 사용할 수 있습니다.
14 | 2. 두 번째 역할은 명령 줄 인터페이스입니다. 온라인 플랫폼과 상호 작용하기 위해 명령 줄 인터페이스를 사용하며 패키지 설치 및 제거가 가능합니다.
15 |
16 | ### 설치
17 |
18 | 노드를 다운로드하면 npm이 시스템에 자동으로 설치됩니다. 노드 설치 후 다음과 같은 명령을 통해 npm이 설치되었는지 확인할 수 있습니다.
19 |
20 | ```bash
21 | node -v
22 | npm -v
23 | ```
24 |
25 | ## yarn
26 |
27 | yarn은 2016년 페이스북에서 개발한 패키지 관리자입니다. 리액트(React)와 같은 프로젝트를 진행하며 겪었던 어려움을 해결하기 위해 개발되었고, npm 레지스트리와 호환하면서 속도나 안정성 측면에서 npm보다 향상되었습니다. [2016년 페이스북이 공개한 아티클](https://engineering.fb.com/2016/10/11/web/yarn-a-new-package-manager-for-javascript/)을 읽어보시면 좋을 것 같습니다.
28 |
29 | ### 설치
30 |
31 | yarn은 npm을 통해 설치합니다.
32 |
33 | ```bash
34 | npm install yarn --global
35 | ```
36 |
37 | 맥 사용자라면 brew를 통해 설치할 수도 있습니다.
38 |
39 | ```bash
40 | brew update
41 | brew install yarn
42 | ```
43 |
44 | ## npm과 yarn의 차이점
45 |
46 | ### 속도
47 |
48 | npm과 yarn의 주요 차이점 중 하나는 패키지 설치 프로세스를 처리하는 방법입니다. npm은 패키지를 한 번에 하나씩 순차적으로 설치합니다. 그에 비해 yarn은 여러 패키지를 동시에 가져오고 설치하도록 최적화되어 있어 패키지 설치 속도 측면에서 yarn이 npm보다 빠릅니다.
49 |
50 | ### 보안
51 |
52 | yarn은 보안 측면에서 npm보다 더 안전한 것으로 알려져 있습니다. npm은 자동으로 패키지에 포함된 다른 패키지 코드를 실행합니다. 이로 인해 보안 시스템에 몇 가지 취약성이 발생하며 나중에 심각한 문제가 발생할 수 있습니다. 반면에 yarn은 yarn.lock 또는 package.json파일에 있는 파일만을 설치합니다. 보안은 yarn의 핵심 기능 중 하나이지만 최근 npm의 업데이트에서 npm의 보안 업데이트도 크게 향상되었습니다.
53 |
54 |
55 | ### 명령어
56 |
57 | | 명령어 | npm | yarn |
58 | | :----------------: | :---------------------------------: | :---------------------------: |
59 | | dependencies 설치 | npm install | yarn |
60 | | 패키지 설치 | npm install [패키지명] | yarn add [패키지명] |
61 | | dev 패키지 설치 | npm install --save-dev [패키지명] | yarn add --dev [패키지명] |
62 | | 글로벌 패키지 설치 | npm install --global [패키지명] | yarn global add [패키지명] |
63 | | 패키지 제거 | npm uninstall [패키지명] | yarn remove [패키지명] |
64 | | dev 패키지 제거 | npm uninstall --save-dev [패키지명] | yarn remove [패키지명] |
65 | | 글로벌 패키지 제거 | npm uninstall --global [패키지명] | yarn global remove [패키지명] |
66 | | 업데이트 | npm update | yarn upgrade |
67 | | 패키지 업데이트 | npm update [패키지명] | yarn upgrade [패키지명] |
68 |
69 |
70 | ## 결론
71 |
72 | npm과 yarn은 모두 종속성을 관리하고 패키지를 관리하기 좋은 툴입니다. 둘 다 지속적으로 관리되고 있으며 폭넓은 사용자 커뮤니티를 가지고 있고, 업데이트를 통해 추가된 기능 덕분에 거의 차이 나지 않게 되었습니다. 결론적으로 둘 중에 무엇을 선택해야 할지는 개인의 취향, 성능(패키지 설치 속도), 커뮤니티에 따라 달라질 수 있을 것 같습니다.
73 |
--------------------------------------------------------------------------------
/docs/pinia/actions.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Actions 🆕
3 | ---
4 |
5 | # Actions
6 |
7 | 피니아의 액션(actions)은 뷰엑스의 뮤테이션(mutations)와 액션(actions)을 합쳐놓은 속성입니다. 기존에 뷰엑스에서 비동기 처리를 하려면 다음의 절차를 따라야 했습니다.
8 |
9 | - actions -> mutations -> state
10 |
11 | 액션에서 API 요청을 하고 받아온 값을 뮤테이션에서 넘긴 후 뮤테이션에서 상태를 변경해 주는 방식이었습니다. 이 방식은 문법적인 측면에서 다소 장황하고 번거로운 측면이 있어 피니아에서는 아래와 같이 단순화 되었습니다.
12 |
13 | - actions -> state
14 |
15 | ## actions 선언
16 |
17 | 액션을 하나 선언해 보겠습니다.
18 |
19 | ```js
20 | export const useStore = defineStore('app', {
21 | state: () => {
22 | return {
23 | count: 0
24 | }
25 | },
26 | actions: {
27 | addCount() {
28 | this.count++;
29 | }
30 | }
31 | });
32 | ```
33 |
34 | 위 코드는 `count` 라는 상태 값을 1씩 증가시키는 `addCount()` 액션 함수를 작성했습니다. 뷰엑스와 다르게 액션 함수 안에서 `this`를 이용하여 바로 `state`에 접근할 수 있습니다.
35 |
36 | 또한, 아래와 같이 비동기 코드도 작성할 수 있습니다.
37 |
38 | ```js
39 | export const useStore = defineStore('app', {
40 | state: () => {
41 | return {
42 | count: 0
43 | }
44 | },
45 | actions: {
46 | async fetchCount() {
47 | const response = await axios.get('/v1/api/productCount');
48 | this.count = response.data;
49 | }
50 | }
51 | });
52 | ```
53 |
54 | ## actions 사용
55 |
56 | 액션은 state, getters와 마찬가지로 컴포넌트에서 다음과 같이 사용합니다.
57 |
58 |
59 |
60 | ```js
61 | export default defineComponent({
62 | setup() {
63 | const store = useStore();
64 | return { store };
65 | }
66 | });
67 | ```
68 |
69 |
70 |
71 | ```js
72 | export default {
73 | setup() {
74 | const store = useStore();
75 | return { store };
76 | }
77 | };
78 | ```
79 |
80 |
81 |
82 | ```html
83 |
84 |
85 |
{{ store.count }}
86 |
87 | ```
88 |
89 | 위 코드에서 더하기 버튼을 누르면 `addCount()` 액션 함수가 실행되면서 스토어의 `count` 상태가 증가됩니다.
--------------------------------------------------------------------------------
/docs/pinia/getters.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getters 🆕
3 | ---
4 |
5 | # Getters
6 |
7 | getters는 여러 컴포넌트에서 사용할 수 있는 [컴퓨티드(computed) 속성](../syntax/computed.md)을 의미합니다.
8 |
9 | ## getters 선언
10 |
11 | getters는 다음과 같이 정의합니다. 뷰엑스에서 정의하던 방식과 동일하게 첫 번째 파라미터로 state를 접근하여 값을 조작합니다.
12 |
13 | ```js
14 | export const useStore = defineStore('app', {
15 | state: () => {
16 | return {
17 | count: 0
18 | }
19 | },
20 | getters: {
21 | doubleCount(state) {
22 | return state * 2;
23 | }
24 | }
25 | });
26 | ```
27 |
28 | ## getters 사용
29 |
30 | 앞에서 선언한 getters는 컴포넌트에서 아래와 같이 사용합니다.
31 |
32 |
33 |
34 | ```js
35 | export default defineComponent({
36 | setup() {
37 | const store = useStore();
38 | return { store };
39 | }
40 | });
41 | ```
42 |
43 |
44 |
45 | ```js
46 | export default {
47 | setup() {
48 | const store = useStore();
49 | return { store };
50 | }
51 | };
52 | ```
53 |
54 |
55 |
56 | ```html
57 |
58 |
{{ store.doubleCount }}
59 |
60 | ```
--------------------------------------------------------------------------------
/docs/pinia/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction 🆕
3 | ---
4 |
5 | # Pinia란?
6 |
7 | [피니아(Pinia)](https://pinia.vuejs.org/)란 [Composition API](../reuse/composition) 기반에서 사용할 수 있는 상태 관리 라이브러리를 의미합니다. Vuex의 최신 버전인 5를 의미하는 라이브러리이기도 합니다.
8 |
9 | ## Vuex랑 뭐가 다를까?
10 |
11 | [뷰엑스(Vuex)](../vuex/concept)는 Vue.js의 상태 관리 패턴이자 라이브러리를 의미합니다. 뷰엑스가 state, getters, mutations, actions를 기반으로 상태를 다뤘다면 피니아는 state, getters, actions로 상태를 관리합니다. 그래서 뷰엑스의 다소 복잡하게 보인 문법들이 단순해 지는 효과가 있습니다.
12 |
13 | 하지만 장점만 있지는 않습니다. 피니아를 사용하려면 컴포지션 API 기반 프로젝트를 사용해야 하며 뷰엑스가 아닌 다른 상태 관리 라이브러리 사용법을 익혀야 하는 번거로움이 있습니다. 따라서, 프로젝트 성격에 맞는 라이브러리를 사용하는 것을 추천합니다.
14 |
15 | :::tip
16 | 👉 이미 운영 중인 Vue 2,3 프로젝트는 Vuex v4.x를 사용
17 | 👉 신규 프로젝트 중 **Composition API만** 사용하는 프로젝트는 Pinia를 사용
18 | :::
19 |
20 | ## Pinia의 주요 속성
21 |
22 | 피니아는 뷰엑스의 최신 버전이기 때문에 뷰엑스와 같은 역할을 합니다. 그리고 기존 뷰엑스의 주요 속성 4개 중 3개를 제공합니다.
23 |
24 | - state
25 | - getters
26 | - actions
27 |
28 | 뷰엑스의 상태를 변경하기 위해 사용했던 mutations가 사라지고 actions 하나로 상태 변경과 비동기 처리를 모두 할 수 있게 되었습니다. 사용하는 입장에서는 다소 번거롭게 느껴졌던 문법이 간소화 되었죠. 뷰엑스와 표로 비교해 보면 다음과 같습니다.
29 |
30 | | Vue | Vuex | Pinia |
31 | |----------|-----------|---------|
32 | | data | state | state |
33 | | computed | getters | getters |
34 | | methods | mutations | actions |
35 | | methods | actions | actions |
36 |
37 | 뷰 인스턴스의 data와 state는 같은 역할을 합니다. data는 해당 컴포넌트의 상태이고 state는 모든 컴포넌트에 공유되는 data(전역 상태)를 의미합니다. computed 속성과 getters 속성 역시 마찬가지로 해당 인스턴스에서만 사용할 것이냐 모든 컴포넌트에서 사용할 것이냐 라는 차이만 있을 뿐 역할은 같습니다. 마지막으로 methods 속성은 mutations + actions과 같은 역할을 했었는데 피니아에서는 actions만 유지하여 문법을 간소화했습니다.
38 |
39 | ## Pinia 설치
40 |
41 | 피니아는 앞서 설명한 것처럼 Composition API로만 작성된 프로젝트에서 사용할 수 있습니다. Vue 2에서 사용하려면 [Composition API 플러그인](https://github.com/vuejs/composition-api)을 별도로 설치해야 하고, Vue 3에서는 컴포지션 API 스타일 코드만 작성해야 합니다.
42 |
43 | 컴포지션 API를 사용하는 뷰 프로젝트에서 아래 명령어로 피니아 라이브러리를 설치합니다.
44 |
45 | ```sh
46 | # npm
47 | npm install pinia
48 | # yarn
49 | yarn add pinia
50 | ```
51 |
52 | ## Pinia 등록
53 |
54 | 피니아를 애플리케이션에서 사용하기 위해서는 main.js 파일에 다음 코드를 추가해야 합니다.
55 |
56 |
57 |
58 | ```js
59 | // main.js
60 | import Vue from 'vue'
61 | import { createPinia, PiniaVuePlugin } from 'pinia'
62 | import App from './App.vue'
63 |
64 | Vue.use(PiniaVuePlugin)
65 | const pinia = createPinia()
66 |
67 | new Vue({
68 | render: h => h(App),
69 | pinia,
70 | }).$mount('#app');
71 | ```
72 |
73 |
74 |
75 | ```js
76 | // main.js
77 | import { createApp } from 'vue'
78 | import { createPinia } from 'pinia'
79 | import App from './App.vue'
80 |
81 | const pinia = createPinia()
82 | createApp(App).use(pinia).mount('#app')
83 | ```
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/docs/pinia/state.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: State 🆕
3 | ---
4 |
5 | # State
6 |
7 | 상태(state)는 여러 컴포넌트에서 공유되는 데이터(data 속성)를 의미합니다.
8 |
9 | ## state 선언
10 |
11 | 피니아 스토어 안에서 다음과 같이 화살표 함수 형태로 정의합니다.
12 |
13 | ```js
14 | export const useStore = defineStore('app', {
15 | state: () => {
16 | return {
17 | count: 0
18 | }
19 | }
20 | });
21 | ```
22 |
23 | ## state 사용
24 |
25 | 컴포넌트의 setup() 함수 안에서 반환한 값으로 상태를 접근합니다.
26 |
27 |
28 |
29 | ```js
30 | export default defineComponent({
31 | setup() {
32 | const store = useStore();
33 | return { store };
34 | }
35 | });
36 | ```
37 |
38 |
39 |
40 | ```js
41 | export default {
42 | setup() {
43 | const store = useStore();
44 | return { store };
45 | }
46 | };
47 | ```
48 |
49 |
50 |
51 | ```html
52 |
53 |
{{ store.count }}
54 |
55 | ```
--------------------------------------------------------------------------------
/docs/pinia/store.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Store 🆕
3 | ---
4 |
5 | ## 스토어 선언
6 |
7 | 피니아의 기본 문법을 사용하기 위해 다음과 같이 스토어를 선언합니다.
8 |
9 | ```js
10 | // ./store/index.js
11 | import { defineStore } from 'pinia'
12 |
13 | export const useStore = defineStore('app', {
14 | state: () => {
15 | return {
16 | message: 'Hello Pinia'
17 | }
18 | }
19 | })
20 | ```
21 |
22 | 위 코드는 store 폴더 아래에 index.js 라는 파일을 만들고 피니아의 기본 코드를 작성했습니다. 피니아 라이브러리에서 제공하는 `defineStore()`를 이용해서 첫 번째 인자로 스토어의 이름을 선언하고 두 번째 인자로 스토어의 내용을 정의합니다.
23 |
24 | 스토어의 내용에는 `message`라는 상태(state)를 하나 정의했습니다.
25 |
26 | :::tip
27 | state를 정의할 때 객체 형태가 아니라 화살표 함수를 사용합니다. 뷰엑스에서는 객체 형태와 화살표 함수 형태가 모두 지원되지만 피니아에서는 화살표 함수 형태로만 작성해야 합니다.
28 | :::
29 |
30 | ## 스토어 사용
31 |
32 | 앞에서 선언한 스토어를 컴포넌트에서 사용해 보겠습니다.
33 |
34 |
35 |
36 | ```html
37 |
38 |
39 |
61 |
62 |
63 |
73 | ```
74 |
75 |
76 |
77 |
78 |
79 | 위 코드는 App 컴포넌트에서 스토어의 상태를 화면에 표시한 코드입니다. 피니아는 이처럼 스토어를 사용하기 위해서 컴포지션 API인 [setup API](../reuse/composition.html#setup)을 꼭 사용해야 합니다. `setup()` 안에서 반환된 store는 템플릿 표현식에서 사용할 수 있기 때문에 `store.message`의 최종 결과는 앞에서 정의한 'Hello Pinia'가 됩니다.
80 |
81 | ## 스토어 작명법
82 |
83 | 피니아의 스토어를 작명할 때는 아래 규칙을 참고합니다.
84 |
85 | 1. `defineStore()`의 첫 번째 인자는 다른 스토어들과 구분되는 유니크한 값이어야 합니다. 예를 들면, 스토어를 2개 등록할 때 이름이 겹치면 안됩니다.
86 |
87 | ```js{1,9}
88 | export const useStore = defineStore('app', {
89 | state: () => {
90 | return {
91 | message: 'Hello Pinia'
92 | }
93 | }
94 | })
95 |
96 | export const useUserStore = defineStore('user', {
97 | state: () => {
98 | return {
99 | user: { name: '캡팡' }
100 | }
101 | }
102 | })
103 | ```
104 |
105 | 2. `defineStore()`를 받는 변수의 이름은 `useXXXStore`으로 작명합니다. 예를 들면, 다음과 같습니다.
106 |
107 | ```
108 | useAppStore
109 | useUserStore
110 | useCounterStore
111 | ```
--------------------------------------------------------------------------------
/docs/pwa/cli-pwa-plugin.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: CLI PWA Plugin
3 | ---
4 |
5 | ## 뷰 CLI의 PWA 플러그인
6 |
7 | 뷰 CLI에서 제공하는 프로그레시브 웹 앱(Progressive Web Apps: PWA) 플러그인은 구글의 [워크 박스(Workbox)](https://developers.google.com/web/tools/workbox) 라이브러리를 기반으로 구성되었습니다. 뷰 CLI의 PWA 플러그인 특징은 다음과 같습니다.
8 |
9 | - 플러그인에서 다루는 서비스 워커 파일은 프로덕션 모드(npm run build)에서만 사용 가능
10 | - 서비스워커 파일을 로컬에서 테스트 하는 경우 npm 빌드 후 [serve](https://www.npmjs.com/package/serve)와 같은 HTTP 서버로 실행
11 |
12 | ## PWA 관련 설정 방법
13 |
14 | 뷰 CLI의 PWA 플러그인에서 제공하는 기능이나 설정을 변경하고 싶다면 다음 둘 중 한 개의 파일을 변경합니다.
15 |
16 | - `vue.config.js`
17 | - `package.json`의 `vue` 속성
18 |
19 | ## PWA 플러그인에서 제공하는 설정
20 |
21 | PWA 플러그인으로 설정할 수 있는 옵션은 다음과 같습니다.
22 |
23 | #### pwa.workboxPluginMode
24 |
25 | [워크 박스의 웹팩 플러그인](https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin)에 의해 다음 2가지 모드가 지원됩니다.
26 |
27 | - `GenerateSW` : 기본 값. 새로 빌드할 때마다 새로운 서비스워커 파일 생성
28 | - `InjectManifest` : 프로젝트 안에 서비스 워커 파일이 있는 경우 프리 캐싱(Precaching)된 코드를 추가하여 사용 가능. 웹 푸시와 같은 기타 서비스 워커 기능을 사용할 때 유용
29 |
30 | :::tip
31 | 각 모드에 대해 더 자세히 알고 싶다면 다음 링크를 참고하세요. [워크박스 플러그인 모드](https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin#which_plugin_to_use)
32 | :::
33 |
34 | #### pwa.workboxOptions
35 |
36 | [워크박스 웹팩 플러그인](https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin)에서
37 | 지원하는 속성 옵션과 동일하게 지원됩니다. ex) swSrc, skipWaiting 등
38 |
39 | #### pwa.iconPaths
40 |
41 | PWA 아이콘 경로를 설정하는 옵션입니다. 기본 값은 다음과 같습니다.
42 |
43 | ```json
44 | {
45 | "favicon32": "img/icons/favicon-32x32.png",
46 | "favicon16": "img/icons/favicon-16x16.png",
47 | "appleTouchIcon": "img/icons/apple-touch-icon-152x152.png",
48 | "maskIcon": "img/icons/safari-pinned-tab.svg",
49 | "msTileImage": "img/icons/msapplication-icon-144x144.png"
50 | }
51 | ```
52 |
--------------------------------------------------------------------------------
/docs/pwa/workbox-caching.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Workbox Caching
3 | ---
4 |
5 | ## PWA의 캐싱이란?
6 |
7 | 브라우저의 캐싱(Cache-Control)과는 다르게 브라우저의 캐시 스토리지(Cache Storage)를 활용하여 웹 리소스를 캐싱하는 기술입니다.
8 |
9 | ## 워크박스 캐싱 전략
10 |
11 | 워크박스로 웹 서비스를 캐싱할 때는 다음 2가지 방법을 알아야 합니다.
12 |
13 | - 프리 캐싱(Precaching)
14 | - 런타임 캐싱(Runtime Caching)
15 |
16 | ## 프리 캐싱
17 |
18 | 프리 캐싱이란 웹 애플리케이션을 접속하기 전에 미리 캐싱해놓는 캐싱 방법을 말합니다.
19 | 워크박스 CLI로 생성한 결과물이나 `workbox-build` 라이브러리에서 아래와 같은 코드를 보면 프리 캐싱이라고 이해하시면 됩니다.
20 |
21 | ```js
22 | // workbox cli
23 | module.exports = {
24 | "globDirectory": "dist/",
25 | "globPatterns": [
26 | "**/*.{css,ico,html,js,json,png}"
27 | ],
28 | "swDest": "dist/sw.js"
29 | };
30 | ```
31 |
32 | ```js
33 | // workbox-build lib
34 | workboxBuild.generateSW({
35 | globDirectory: 'build',
36 | globPatterns: [
37 | '**/*.{html,json,js,css}',
38 | ],
39 | swDest: 'build/sw.js',
40 | });
41 | ```
42 |
43 | ## 런타임 캐싱
44 |
45 | 런타임 캐싱이란 웹 애플리케이션을 동작 시킬 때 발생하는 요청에 대해 서버 응답을 캐시 스토리지에 저장하는 캐싱 방법을 말합니다.
46 | 보통 이미지와 같은 리소스는 파일 용량이 커서 처음 페이지 접속할 때 서비스 워커 설치 시간이 길어지므로 런타임 캐싱으로 하는게 좋습니다.
47 |
48 | ```js
49 | // workbox-build lib
50 | workboxBuild.generateSW({
51 | // ...
52 | runtimeCaching: [{
53 | urlPattern: /\.(?:png|jpg|jpeg|svg)$/,
54 | handler: 'CacheFirst',
55 | options: {
56 | cacheName: 'my web app images',
57 | expiration: {
58 | maxEntries: 10,
59 | },
60 | },
61 | }],
62 | });
63 | ```
64 |
65 | ## 캐싱 전략
66 |
67 | 서비스 워커의 캐싱 전략은 다음 5개가 있습니다.
68 |
69 | - Cache First : 네트워크 요청과 캐싱 중 항상 캐시를 먼저 접근하는 방식
70 | - Cache Only : 캐싱 파일만 확인하고 없으면 에러를 뱉는 방식
71 | - Network First : 항상 캐싱보다는 네트워크 요청을 먼저 진행하는 방식
72 | - Network Only : 해당 파일에 대해서는 캐싱 파일의 유무와 관계 없이 항상 네트워크 요청만 하는 방식
73 | - StaleWhileRevalidate : 캐싱을 먼저 시도하고 없으면 네트워크 요청을 진행하는 방식. 프로필 이미지와 같이 자주 업데이트 되면서 최신 데이터가 아니어도 되는 데이터 적용하면 좋음
74 |
75 | ## 참고 자료
76 |
77 | - [Workbox 공식 홈페이지](https://developers.google.com/web/tools/workbox/guides/get-started)
78 | - [Workbox Stratigies](https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-strategies)
79 | - [Offline Cookbook](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook#cache-falling-back-to-network)
--------------------------------------------------------------------------------
/docs/pwa/workbox.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Workbox
3 | ---
4 |
5 | ## 워크박스(Workbox)란?
6 |
7 | PWA의 캐싱(caching) 기능을 편하게 구현할 수 있도록 지원되는 표준 PWA 라이브러리.
8 | [sw-precaching], [sw-toolbox] 라이브러리가 지원하던 기능들을 통합하여 제공한다.
9 |
10 | ## CLI로 워크박스 시작하기
11 |
12 | 워크박스 라이브러리의 사용 방법을 익히기 위해서 CLI로 간단하게 파일을 캐싱해보겠습니다.
13 | 먼저 CLI를 아래 명령어로 설치합니다.
14 |
15 | ```bash
16 | npm install workbox-cli --global
17 | ```
18 |
19 | 정상적으로 설치가 되었으면 `workbox --help` 명령어가 인식될 것입니다.
20 |
21 | ## 워크박스 CLI로 서비스 워커 생성하기
22 |
23 | PWA를 적용하고 싶은 웹 애플리케이션의 프로젝트에서 아래와 같이 워크박스 위저드 명령어를 입력합니다.
24 |
25 | ```bash
26 | workbox wizard
27 | ```
28 |
29 | 위 명령어를 입력하면 아래와 같이 몇 가지의 질문이 나옵니다. 그 질문엔 아래와 같이 대답합니다.
30 |
31 | - 웹 애플리케이션의 루트가 어디인지? `app/`
32 | - 어떤 파일 유형들을 프리캐싱(pre-caching)할 것인지? `html, css, js, jpg, png`
33 | - 위 옵션들이 적용된 서비스 워커 파일이 생성될 위치? `dist/sw.js`
34 | - 위 구성 정보들을 어디에 저장할 것인가? `workbox-config.js`
35 |
36 | ## 워크박스 CLI의 generateSW 모드
37 |
38 | 앞의 위저드로 생성한 워크 박스의 구성 정보는 `workbox-config.js`에 담기게 됩니다.
39 | 이 때 다음 명령어를 입력하면 위 구성 정보가 담긴 서비스 워커 파일을 생성할 수 있습니다.
40 |
41 | ```bash
42 | workbox generateSW ./workbox-config.js
43 | ```
44 |
45 | 이렇게 생성된 서비스 워커 파일에는 워크박스의 프리 캐싱과 런타임 캐싱 기능이 포함되어 있습니다.
46 |
47 | :::tip
48 | 다만, 웹 푸시 기능이 필요한 경우에는 적합하지 않은 옵션입니다. 웹 푸시는 `injectManifest` 옵션을 활용하세요.
49 | :::
50 |
51 | ## 워크박스 CLI의 injectManifest 모드
52 |
53 | 워크박스 injectManifest 모드는 generateSW 모드와는 다르게 개발자가 좀 더 서비스워커 파일을 직접 조작하거나 내용을 수정할 수 있습니다. 파일 캐싱 이외에 웹 푸시 API와 같은 네이티브 API 기능을 구현하고 싶을 때 적합한 모드입니다.
54 |
55 | 개발자가 구현해놓은 서비스 워커 파일을 읽어 설정 파일(workbox-config.js)에 설정된 캐싱 정보를 추가하고 새 서비스 워커 파일을 생성해줍니다. 명령어는 다음과 같습니다.
56 |
57 | ```bash
58 | workbox injectManifest ./workbox-config.js
59 | ```
--------------------------------------------------------------------------------
/docs/reuse/mixins-vs-hoc.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Mixins vs HOC
3 | ---
4 |
5 | # 믹스인과 하이 오더 컴포넌트 비교 분석
6 |
7 | ## HOC vs Mixins
8 |
9 | 하이 오더 컴포넌트는 리액트 진영의 함수형 프로그래밍에서 기반한 컴포넌트 개발 패턴입니다.
10 | 컴포넌트의 코드를 재사용하기 위한 방법이기도 하지만 캡슐화(encapsulation)과 컴포넌트 추상화를 구현하는 방법이기도 한데요.
11 | 컴포넌트의 로직을 훼손하지 않고 재사용성을 최대한 끌어올리겠다는 전략이기도 합니다.
12 | 예를 한번 들어볼게요.
13 |
14 | ## HOC를 이용한 접근 방식의 예
15 |
16 | 일반적으로 HOC를 이용하여 컴포넌트를 구현하게 되면 다음과 같이 컴포넌트 관계에서 층이 하나 더 생깁니다.
17 |
18 | - 일반 : 상위 - 하위
19 | - HOC : 상위 - **HOC** - 하위
20 |
21 | 위와 같이 HOC를 이용하여 컴포넌트를 개발해 나가는 경우 상위와 하위의 컴포넌트 로직은 변경하지 않은 채 기능을 확장해 나갈 수 있습니다.
22 | 가장 간단한 예로는 상위 컴포넌트에 특정 이미지를 로딩하기 위한 url만 주입하고, 하위에서는 그 url을 뿌리기만 하면 되겠네요.
23 | 추가적인 로직은 HOC에서 해결하면 되겠습니다.
24 |
25 | ## Mixins를 이용한 접근 방식
26 |
27 | 위와 같이 HOC를 이용한 접근 방식은 컴포넌트의 레이어를 복잡하게 만듭니다. 달리 말해 컴포넌트의 props, event 등을 넘겨야 하는 코드가 많아지죠.
28 | 이에 비해 mixins는 문법도 간단하고 입문자에게 버거운 HOC 사고 방식을 하지 않아도 되는 이점이 있습니다.
29 | 물론 컴포넌트 기능 테스트 측면에서는 HOC가 mixins보다 유리합니다.
30 | 관심사의 분리(sepration of concerns)라는 측면에서는 컴포넌트의 역할이 깨끗이 분리가 되면서 기능을 확장할 수 있기 때문입니다.
31 |
32 | ## 결론
33 |
34 | 따라서, 개인의 선호도 차이겠지만 뷰에서는 mixins나 scoped slot을 최대한 활용하고 그래도 함수형 프로그래밍이나 컴포넌트 재사용성을 극대화하고 싶다면
35 | HOC를 활용하여 컴포넌트 코드를 재활용하는 방법을 추천합니다.
--------------------------------------------------------------------------------
/docs/reuse/mixins.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Mixins
3 | ---
4 |
5 | # 믹스인
6 |
7 | 믹스인(Mixins)은 여러 컴포넌트 간에 공통으로 사용하고 있는 로직, 기능들을 재사용하는 방법입니다. 믹스인에 정의할 수 있는 재사용 로직은 data, methods, created 등과 같은 컴포넌트 옵션입니다.
8 |
9 | ## 믹스인 코드 형식
10 |
11 | 믹스인 문법은 아래와 같습니다.
12 |
13 | ```js
14 | var HelloMixins = {
15 | // 컴포넌트 옵션 (data, methods, created 등)
16 | };
17 |
18 | new Vue({
19 | mixins: [HelloMixins]
20 | })
21 | ```
22 |
23 | 위와 같이 믹스인을 주입할 컴포넌트에 `mixins` 속성을 선언하고 배열 `[]` 안에 주입할 믹스인들을 추가합니다.
24 |
25 | ## 믹스인 사용 예시
26 |
27 | 그럼 실제로 있을 법한 믹스인 코드 예시를 보겠습니다. 웹 애플리케이션을 구현할 때 많이 사용되는 다이얼로그(모달 혹은 팝업 창)의 열기, 닫기 로직을 믹스인에 정의했습니다.
28 |
29 | ```js
30 | var DialogMixin = {
31 | data() {
32 | return {
33 | dialog: false
34 | }
35 | },
36 | methods: {
37 | showDialog() {
38 | this.dialog = true;
39 | },
40 | closeDialog() {
41 | this.dialog = false;
42 | }
43 | }
44 | };
45 | ```
46 |
47 | `DialogMixin`에는 다이얼로그의 표시 상태를 나타내는 `dialog` 데이터와 다이얼로그를 열거나 닫는 메서드 `showDialog()`, `closeDialog()`가 정의되어 있습니다. 이제 이 믹스인을 컴포넌트에 어떻게 주입할까요? 아래 코드를 보겠습니다.
48 |
49 | ```html
50 |
51 |
69 | ```
70 |
71 | 믹스인을 사용할 수준이 되면 보통은 싱글 파일 컴포넌트 체계에서 ES6를 능숙하게 사용하고 있을겁니다. 위의 코드는 ES6의 모듈화 문법을 이용해 믹스인을 다른 파일에서 가져와서 주입하는 코드입니다. `submitForm()` 메서드에서 HTTP POST 요청을 보내고 나면 `this.closeDialog()`로 메서드를 호출하는데 이 메서드는 믹스인에 의해 주입된 메서드입니다. 따라서 LoginForm 컴포넌트에 없더라도 사용할 수 있습니다.
--------------------------------------------------------------------------------
/docs/reuse/plugins.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Plugins 🆕
3 | ---
4 |
5 | # 플러그인
6 |
7 | 플러그인은 애플리케이션에서 자주 사용될만한 속성, 함수, 라이브러리 등의 사용성을 높여주는 기능입니다. 뷰 라우터, 뷰엑스 등의 코어 라이브러리가 이미 뷰 플러그인 형태로 제공되고 있습니다.
8 |
9 | ## 플러그인 사용 방법
10 |
11 | 뷰로 개발하기 위한 외부 라이브러리들을 검색하다 보면 아래와 같은 코드를 마주친 적이 있을 겁니다.
12 |
13 |
14 |
15 | ```js
16 | Vue.use(VueRouter);
17 | Vue.use(Vuex);
18 | ```
19 |
20 |
21 |
22 | ```js
23 | const app = Vue.createApp();
24 |
25 | app.use(VueRouter);
26 | app.use(Vuex);
27 | ```
28 |
29 |
30 |
31 | 여기서 `Vue.use()` 코드가 바로 플러그인을 설치하여 사용하는 코드입니다. 플러그인을 한번 설치하고 나면 뷰 인스턴스의 내부에 플러그인에 정의한 기능이 추가됩니다. 그러고 나면 컴포넌트 내부에서 this로 해당 기능을 편하게 접근할 수 있습니다. 이런 이유로 자주 사용되는 라이브러리나 기능들은 플러그인으로 정의하여 사용하는 것이 좋습니다.
32 |
33 | ## 플러그인 구현 방법
34 |
35 | 그럼 플러그인을 어떻게 구현할 수 있는지 살펴보겠습니다. 아래는 간단한 플러그인 제작 코드입니다.
36 |
37 | ```js
38 | // ChartPlugin.js
39 | import Chart from 'chartjs';
40 |
41 | export default {
42 | install(Vue, options) {
43 | Vue.prototype.ChartJS = Chart;
44 | }
45 | }
46 | ```
47 |
48 | 위 코드는 chartjs라는 외부 라이브러리를 npm 방식으로 프로젝트에 설치한 후 해당 라이브러리를 플러그인으로 사용하는 코드입니다. 차트 라이브러리를 불러와 `Chart`라는 변수에 담았고 뷰 플러그인을 설치(install)할 때 뷰의 프로토타입 속성으로 해당 변수를 연결하는 코드입니다. 따라서 아래와 같이 뷰 인스턴스를 정의하기 전에 플러그인을 설치하면 컴포넌트에서 매번 차트 라이브러리를 불러오지 않고도 사용할 수 있습니다.
49 |
50 |
51 |
52 | ```js
53 | import ChartPlugin from './ChartPlugin.js';
54 |
55 | Vue.use(ChartPlugin);
56 |
57 | new Vue({
58 | // ...
59 | });
60 | ```
61 |
62 |
63 |
64 | ```js
65 | import ChartPlugin from './ChartPlugin.js';
66 |
67 | const app = Vue.createApp({
68 | // ...
69 | });
70 | app.use(ChartPlugin);
71 | ```
72 |
73 |
74 |
75 | 특정 컴포넌트에서 차트를 사용하고 싶을 때 아래와 같이 `this.ChartJS`로 차트를 호출합니다.
76 |
77 | ```html {5}
78 |
79 |
88 | ```
89 |
90 | ::: tip
91 | 플러그인 변수명은 `$_`가 좋습니다. 뷰 라이브러리 내부적으로 사용하는 private 변수는 `_`를 사용하고 있고, 사용자에게 노출시키는 인스턴스 관련 속성은 `$`를 사용하고 있기 때문입니다. 이 내용은 뷰 공식 문서의 스타일 가이드를 따랐습니다.
92 | :::
93 |
--------------------------------------------------------------------------------
/docs/reuse/scoped-slot.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Scoped Slot
3 | ---
4 |
5 | # 스콥드 슬롯
6 |
7 | 슬롯(slot)이 컴포넌트 템플릿의 재사용성을 늘려주기 위한 기능이라면 스콥드 슬롯(Scoped slot)은 컴포넌트 데이터의 재사용성을 높여주는 기능입니다. 일반적으로 뷰에서는 프롭스 속성이나 이벤트 발생과 같은 컴포넌트 통신 방식을 제외하고는 다른 컴포넌트의 값을 참조할 수 없습니다. **하지만 스콥드 슬롯은 하위 컴포넌트의 값을 상위 컴포넌트에서 접근하여 사용할 수 있습니다.**
8 |
9 | ## 스콥드 슬롯 코드 형식
10 |
11 | 스콥드 슬롯을 사용하기 위해서는 기본적으로 상위, 하위 2개의 컴포넌트가 필요하고 각각의 컴포넌트에 아래와 같이 구현해야합니다.
12 |
13 | ```html
14 |
15 |
16 | ```
17 |
18 | 먼저 하위 컴포넌트에서 `` 태그를 정의하고 `v-bind` 디렉티브의 약식 문법인 `:`를 이용하여 하위 컴포넌트의 데이터를 연결합니다. 컴포넌트 통신 방법에서 배운 프롭스 속성 전달 방법과 비슷한 형식입니다.
19 |
20 | 이제 상위 컴포넌트의 코드를 보겠습니다.
21 |
22 | ```html
23 |
24 |
25 |
26 | {{ 임의의 변수.상위 컴포넌트로 전달할 속성 이름 }}
27 |
28 |
29 | ```
30 |
31 | 하위 컴포넌트인 `` 태그에 집중합니다. 위에서 `` 태그를 미리 정의해놨기 때문에 `` 태그 사이에 들어가는 코드는 모두 슬롯으로 처리가 됩니다. 여기서 `slot-scope`라는 속성으로 하위 컴포넌트에서 올려보내준 데이터를 전달받을 수 있습니다.
32 |
33 | ::: tip
34 | `slot-scope` 속성이 정의된 `` 태그는 실제 DOM이 아니라 속성을 전달받기 위한 껍데기일 뿐입니다. 실제로 화면 렌더링이 되고 나면 `` 태그는 가시적인 태그로 변환되지 않습니다.
35 | :::
36 |
37 | 아직 안 와닿으실 수 있으니 실제 예제를 같이 살펴보겠습니다.
38 |
39 | ## 스콥드 슬롯 코드 예제
40 |
41 | 앞에서 살펴본 코드 형식을 참고하여 간단한 예제를 제작하였습니다. 상위 컴포넌트는 App.vue이고 하위 컴포넌트는 ChildComponent.vue 입니다.
42 |
43 | 하위 컴포넌트의 코드부터 보겠습니다.
44 |
45 | ```html {5}
46 |
47 |
48 |
49 |
Child Component
50 |
51 |
52 |
53 |
54 |
63 | ```
64 |
65 | 하위 컴포넌트에 `` 태그를 정의하고 v-bind 디렉티브(`:`)를 이용하여 `message` 속성을 전달하였습니다.
66 |
67 | 이번엔 상위 컴포넌트의 코드를 살펴보겠습니다.
68 |
69 | ```html {5,6,7}
70 |
71 |
72 |
79 |
80 |
81 |
90 | ```
91 |
92 | 하위 컴포넌트인 `` 태그의 슬롯 영역에 `slot-scope` 속성을 정의하였습니다. 여기서 사용한 변수 `scopeProps`는 하위 컴포넌트에서 올려준 값을 받기 위한 임의의 변수입니다. 임의의 변수이기 때문에 원하는 변수 명을 지정하여 사용할 수 있습니다. `scopeProps`에는 하위 컴포넌트에서 올려준 값 `message`이 들어있습니다.
93 |
94 | 위의 코드를 실행하면 결론적으로 `
` 제목 태그와 함께 하위 컴포넌트의 `message` 값인 Hello가 화면에 출력됩니다.
95 |
96 | :::tip
97 | 뷰 버전 2.6에서 슬롯의 문법이 `v-slot` 디렉티브로 바뀐 이유는 스콥스 슬롯의 변수 접근 범위 때문입니다.
98 | 일반 HTML 태그에서 스콥드 슬롯을 적용하면 이해하는데 크게 문제가 없으나 컴포넌트 태그에 스콥드 슬롯을 적용하는 경우 변수의 접근 범위에 대해 혼란이 있을 수 있습니다.
99 | 따라서, 기존 레거시 코드와 겹치지 않게 `v-slot` 및 `#`으로 개편하였습니다.
100 |
101 | 자세한 내용은 [여기](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0001-new-slot-syntax.md)를 참고하세요.
102 | :::
--------------------------------------------------------------------------------
/docs/syntax/filters.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Filters
3 | ---
4 |
5 | # 뷰 필터
6 |
7 | 뷰의 필터는 화면에 표시되는 텍스트의 형식을 쉽게 변환해주는 기능입니다. 가장 간단하게는 단어의 대문자화부터 다국어, 국제 통화 표시 등 다양하게 사용할 수 있습니다.
8 |
9 | ## 필터 사용 방법
10 |
11 | 필터틀 사용하는 방법은 아래와 같습니다.
12 |
13 | ```html
14 |
15 | {{ message | capitalize }}
16 |
17 | ```
18 |
19 | ```js
20 | new Vue({
21 | el: '#app',
22 | data: {
23 | message: 'hello'
24 | },
25 | filters: {
26 | capitalize: function(value) {
27 | if (!value) return ''
28 | value = value.toString()
29 | return value.charAt(0).toUpperCase() + value.slice(1)
30 | }
31 | }
32 | })
33 | ```
34 |
35 | 위의 코드를 실행하면 Hello 텍스트가 화면에 출력됩니다. 필터를 쓰지 않았다면 hello가 출력되었겠죠.
36 |
37 | ## 필터 등록 패턴
38 |
39 | 위와 같이 `filters` 속성을 이용하여 각 컴포넌트에 필터를 등록하는 방법도 있지만, 실제로는 대부분 filters.js 파일을 별도로 분리하여 사용합니다. 아래와 같이 말이죠.
40 |
41 | ```js
42 | // filters.js
43 | export function capitalize() {
44 | // ..
45 | }
46 |
47 | export function translate() {
48 | // ..
49 | }
50 | ```
51 |
52 | ```js
53 | // main.js
54 | import Vue from 'vue';
55 | import * as filters from 'filters.js';
56 |
57 | Object.keys(filters).forEach(function(key) {
58 | Vue.filter(key, filters[key]);
59 | });
60 |
61 | new Vue({
62 | // ..
63 | })
64 | ```
65 |
66 | :::danger
67 | 필터는 Vue 3부터 사용할 수 없습니다.
68 | :::
--------------------------------------------------------------------------------
/docs/syntax/methods.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Methods
3 | ---
4 |
5 | # 뷰 메서드
6 |
7 | 뷰의 메서드는 특정 기능 별로 묶을 수 있는 자바스크립트 함수를 의미합니다. 메서드는 흔히 뷰 템플릿의 버튼 이벤트 처리부터 HTTP 통신까지 다양한 성격의 코드로 구성됩니다.
8 |
9 | ## 메서드 코드 형식
10 |
11 | 메서드를 선언하는 방법은 아래와 같습니다.
12 |
13 |
14 |
15 | ```js
16 | new Vue({
17 | methods: {
18 | // ..
19 | }
20 | })
21 | ```
22 |
23 |
24 |
25 | ```js
26 | Vue.createApp({
27 | methods: {
28 | // ..
29 | }
30 | })
31 | ```
32 |
33 |
34 |
35 | ## 메서드 예시 1 - 기본
36 |
37 | 메서드를 이용해서 버튼 클릭 이벤트를 처리해보겠습니다.
38 |
39 | ```html
40 |
41 | ```
42 |
43 |
44 |
45 | ```js
46 | new Vue({
47 | methods: {
48 | clickButton() {
49 | alert('clicked');
50 | }
51 | }
52 | })
53 | ```
54 |
55 |
56 |
57 | ```js
58 | Vue.createApp({
59 | methods: {
60 | clickButton() {
61 | alert('clicked');
62 | }
63 | }
64 | })
65 | ```
66 |
67 |
68 |
69 | 위의 click me 버튼을 클릭하면 경고창이 뜨면서 clicked 라는 메시지가 표시됩니다.
70 |
71 | ## 메서드 예시 2 - 응용
72 |
73 | 메서드의 내용에는 단순히 화면 조작을 위한 기능 뿐만 아니라 특정 로직을 담아놓는 장소로도 활용할 수 있습니다.
74 |
75 | ```html
76 |
77 | ```
78 |
79 | 위처럼 Refresh 라는 버튼을 하나 만들고 클릭 했을 때 `displayProducts()` 메서드가 수행될 수 있게 디렉티브로 연결합니다.
80 |
81 |
82 |
83 | ```js
84 | new Vue({
85 | data() {
86 | return {
87 | products: []
88 | }
89 | },
90 | methods: {
91 | displayProducts() {
92 | this.fetchData();
93 | // ..
94 | },
95 | fetchData() {
96 | axios.get('/products').then(function(response) {
97 | this.products = response.data;
98 | }).catch(function(error) {
99 | alert(error);
100 | });
101 | }
102 | }
103 | })
104 | ```
105 |
106 |
107 |
108 | ```js
109 | Vue.createApp({
110 | data() {
111 | return {
112 | products: []
113 | }
114 | },
115 | methods: {
116 | displayProducts() {
117 | this.fetchData();
118 | // ..
119 | },
120 | fetchData() {
121 | axios.get('/products').then(function(response) {
122 | this.products = response.data;
123 | }).catch(function(error) {
124 | alert(error);
125 | });
126 | }
127 | }
128 | })
129 | ```
130 |
131 |
132 |
133 | Refresh 버튼을 클릭하고 나면 `displayProducts()` 메서드가 `fetchData()`를 호출합니다. 이런 식으로 메서드를 연결해서 사용할 수도 있으며 이렇게 하면 특정 기능 별로 메서드를 분리할 수 있어 코드를 중복해서 작성하지 않고 재활용하기가 수월합니다.
--------------------------------------------------------------------------------
/docs/syntax/watch.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Watch
3 | ---
4 |
5 | # watch 속성
6 |
7 | watch 속성은 특정 데이터의 변화를 감지하여 자동으로 특정 로직을 수행해주는 속성입니다.
8 |
9 | ## watch 코드 형식
10 |
11 | watch 속성의 구현 코드 형식은 다음과 같습니다.
12 |
13 |
14 |
15 | ```js
16 | new Vue({
17 | data() {
18 | return {
19 | message: 'Hello'
20 | }
21 | },
22 | watch: {
23 | message: function(value, oldValue) {
24 | console.log(value);
25 | }
26 | }
27 | })
28 | ```
29 |
30 |
31 |
32 | ```js
33 | Vue.createApp({
34 | data() {
35 | return {
36 | message: 'Hello'
37 | }
38 | },
39 | watch: {
40 | message: function(value, oldValue) {
41 | console.log(value);
42 | }
43 | }
44 | })
45 | ```
46 |
47 |
48 |
49 | 위 코드는 `message`라는 데이터에 watch 속성을 지정한 코드입니다. `message`의 데이터가 변할 때마다 watch 속성에 정의한 `message()` 함수가 실행되면서 콘솔에 변한 데이터를 출력합니다.
50 |
51 | ## watch 실용 문법
52 |
53 | 앞의 예시에서 살펴본 기본적인 문법 말고도 아래와 같이 다양한 형태로 watch 속성을 구현할 수 있습니다.
54 |
55 | ### 1. watch 속성에 메서드 함수를 연결
56 |
57 | watch 대상 속성에 함수를 연결하는 대신 메서드 함수를 연결할 수 있습니다.
58 |
59 |
60 |
61 | ```js{13}
62 | new Vue({
63 | data() {
64 | return {
65 | message: 'Hello'
66 | }
67 | },
68 | methods: {
69 | logMessage() {
70 | console.log(this.message);
71 | }
72 | },
73 | watch: {
74 | 'message': 'logMessage' // 대상 속성과 메서드 함수를 매칭
75 | }
76 | })
77 | ```
78 |
79 |
80 |
81 | ```js{13}
82 | Vue.createApp({
83 | data() {
84 | return {
85 | message: 'Hello'
86 | }
87 | },
88 | methods: {
89 | logMessage() {
90 | console.log(this.message);
91 | }
92 | },
93 | watch: {
94 | 'message': 'logMessage' // 대상 속성과 메서드 함수를 매칭
95 | }
96 | })
97 | ```
98 |
99 |
100 |
101 | ### 2. 핸들러와 초기 실행 옵션
102 |
103 | watch 대상 속성에 아래와 같이 `handler()`와 `immediate` 속성을 정의할 수 있습니다.
104 |
105 |
106 |
107 | ```js{9-12}
108 | new Vue({
109 | data() {
110 | return {
111 | message: 'Hello'
112 | }
113 | },
114 | watch: {
115 | 'message': {
116 | handler(value, oldValue) {
117 | console.log(value);
118 | },
119 | immediate: true // 컴포넌트가 생성되자마자 즉시 실행
120 | }
121 | }
122 | })
123 | ```
124 |
125 |
126 |
127 | ```js{9-12}
128 | Vue.createApp({
129 | data() {
130 | return {
131 | message: 'Hello'
132 | }
133 | },
134 | watch: {
135 | 'message': {
136 | handler(value, oldValue) {
137 | console.log(value);
138 | },
139 | immediate: true // 컴포넌트가 생성되자마자 즉시 실행
140 | }
141 | }
142 | })
143 | ```
144 |
145 |
--------------------------------------------------------------------------------
/docs/testing/api.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: API
3 | ---
4 |
5 | # 자주 사용하는 API 목록
6 |
7 | 단위 테스트 케이스 작성을 위해 자주 사용하는 API 목록입니다.
8 |
9 | ## Shallow Rendering
10 |
11 | 테스트 할 컴포넌트의 기능만 테스트하고 하위 컴포넌트와는 분리해주는 테스트 API
12 |
13 | ```js
14 | import { shallowMount } from '@vue/test-utils'
15 | import Component from './component'
16 |
17 | describe('Component', () => {
18 | test('is a Vue instance', () => {
19 | const wrapper = shallowMount(Component);
20 | expect(wrapper.isVueInstance()).toBeTruthy();
21 | })
22 | })
23 | ```
24 |
25 | > mount a component without rendering its child components
26 |
27 | ## Mount
28 |
29 | 테스트할 컴포넌트의 하위 컴포넌트의 동작까지 함께 테스트 하는 API
30 |
31 | ```js
32 | // helloworld.test.js
33 | import { mount } from '@vue/test-utils';
34 | import HelloWorld from './HelloWorld.vue';
35 |
36 | test('HelloWorld Component', () => {
37 | const wrapper = mount(HelloWorld);
38 | expect(wrapper.vm.message).toBe('Vue!');
39 | });
40 | ```
--------------------------------------------------------------------------------
/docs/testing/component-tutorial-1.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Tutorial - User Interaction
3 | ---
4 |
5 | # 튜토리얼 1 - 사용자 키보드 & 마우스 입력 테스트하기
6 |
7 | 실제 뷰 컴포넌트 테스트 코드를 작성하기 위해 간단히 사용자 이벤트를 처리하는 코드부터 살펴보겠습니다.
8 |
9 | ## 사용자 로그인
10 |
11 | 아래와 같이 간단한 로그인 컴포넌트가 있다고 가정하겠습니다.
12 |
13 | ```html
14 |
15 | ```
--------------------------------------------------------------------------------
/docs/testing/coverage.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Coverage
3 | ---
4 |
5 | # 테스트 커버리지(범위)
6 |
7 | ## 테스팅의 목적
8 |
9 | 1. 코드를 변경할 때 두려워하지 않기 위해
10 | 2. 코드 품질 보장
11 | 3. 도큐먼트로써의 역할
12 |
13 | > Confidence to change / Removal of Fear + High Code Quality + Well-documented Code = Developer Happiness
14 |
15 | ## 뷰 컴포넌트 테스트
16 |
17 | 
18 |
19 | 뷰 컴포넌트 테스트 코드를 작성할 때 고민할 지점들은 다음과 같습니다.
20 |
21 | - 컴포넌트의 입력 값 : props, user interaction, lifecycle methods
22 | - 컴포넌트의 출력 값 : events, rendered output, connection with children
23 |
24 | 테스트 할 필요가 없는 지점들
25 |
26 | - 컴포넌트의 구체적인 로직 (비즈니스 로직)
27 | - 프레임워크 자체의 기능들 (prop rendering, prop validation 등)
28 |
29 | ## 뷰 컴포넌트 테스팅 기법
30 |
31 | - Integration 테스트 : 특정 컴포넌트에 종속된 하위 컴포넌트까지 모두 컴포넌트의 테스트 범위로 간주. `mount()` API 사용. 특정 기능의 전체 흐름을 모두 테스트 케이스로 작성.
32 | - Shallow 테스트 : 특정 컴포넌트에 등록된 하위 컴포넌트는 신경쓰지 않고 해당 컴포넌트의 기능만 테스트. `shallowMount()` API 사용. 특정 기능의 흐름을 잘게 분할해서 테스트 케이스로 작성.
33 |
34 | ## 참고 사이트
35 |
36 | 가이드 작성 후 링크 제거. 제거할 때 가이드 꼼꼼히 확인
37 |
38 | - [프로처럼 테스팅 하기](https://vueschool.io/articles/series/testing-like-a-pro-in-javascript/)
39 | - [뷰 컴포넌트 테스트하기](https://vueschool.io/articles/vuejs-tutorials/5-testing-a-vue-component/)
40 | - [에드의 뷰 테스팅 발표](https://www.meetupfeed.com/talk/unit-testing-vue-components-why-test-what-to-test-and-how-to-test-vue-components)
41 | - [스냅샷 테스팅 링크](https://vue-test-utils.vuejs.org/guides/#testing-single-file-components-with-jest)
42 |
--------------------------------------------------------------------------------
/docs/testing/getting-started.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getting Started
3 | ---
4 |
5 | # 뷰 테스트 코드 시작하기
6 |
7 | 앞에서 설치한 뷰 테스트 유틸 라이브러리를 이용하여 간단한 테스트 코드를 작성해보겠습니다.
8 |
9 | ## 뷰 컴포넌트 테스트 코드 예시
10 |
11 | 아래의 코드를 가지고 `npm t`를 실행합니다.
12 |
13 | ```html
14 |
15 |
16 |
Hello {{ message }}
17 |
18 |
19 |
28 | ```
29 |
30 | ```js
31 | // helloworld.test.js
32 | import Vue from 'vue';
33 | import HelloWorld from './HelloWorld.vue';
34 |
35 | test('HelloWorld Component', () => {
36 | const cmp = new Vue(HelloWorld).$mount();
37 | expect(cmp.message).toBe('Vue!');
38 | });
39 | ```
40 |
41 | 코드가 실행되면 HelloWorld 컴포넌트가 정상적으로 로딩되면서 테스트가 통과됩니다.
42 |
43 | :::tip
44 | `HelloWorld` 컴포넌트 파일 경로를 주의하세요.
45 | :::
46 |
47 | ## 뷰 테스트 유틸 API를 이용한 컴포넌트 테스트
48 |
49 | 앞에서 살펴본 테스트 코드의 번거로운 점은 컴포넌트의 기능을 테스트하기 위해
50 | 매번 뷰 인스턴스를 생성하고 `$mount()` API를 호출해야 한다는 점입니다.
51 |
52 | 다행히도 뷰 테스트 유틸 라이브러리에서 이 반복 작업을 편하게 해주는 API를 제공합니다.
53 | 바로 `shallowMount()`와 `mount()`입니다. 이 두 API의 차이점은 [Testing Applications - API](/testing/api.html) 챕터에서 살펴보도록 하고, 먼저 `shallowMount()` API를 사용해보겠습니다.
54 |
55 | 앞에서 살펴본 코드에 `shallowMount()` API를 적용해봤습니다.
56 |
57 | ```js {2,6}
58 | // helloworld.test.js
59 | import { shallowMount } from '@vue/test-utils';
60 | import HelloWorld from './HelloWorld.vue';
61 |
62 | test('HelloWorld Component', () => {
63 | const wrapper = shallowMount(HelloWorld);
64 | expect(wrapper.vm.message).toBe('Vue!');
65 | });
66 | ```
67 |
68 | 뷰 테스트 라이브러리에서 `shallowMount()` API를 불러온 후 HelloWorld 컴포넌트의 `message` 속성을 테스트 하였습니다. 앞서 살펴본 코드와 차이나는 부분은 아래와 같습니다.
69 |
70 | ```js
71 | // 뷰 테스트 유틸 API를 사용하지 않은 경우
72 | const cmp = new Vue(HelloWorld).$mount();
73 | ```
74 |
75 | ```js
76 | // 뷰 테스트 유틸 API를 사용한 경우
77 | const wrapper = shallowMount(HelloWorld);
78 | ```
79 |
80 | `new Vue().$mount()`로 접근하던 형식을 `mount()`로 간소화 하였습니다.
81 |
82 | ## 컴포넌트 테스팅 관련 API
83 |
84 | 앞에서 살펴본 `shallowMount()` API 이외에 `mount()` API도 테스트 할 때 자주 사용됩니다.
85 | `shallowMount()`는 지정된 컴포넌트의 내용만 테스트할 때 사용하고,
86 | `mount()`는 해당 컴포넌트에 등록된 하위 컴포넌트의 내용들까지 확인할 때 사용합니다.
87 | 더 자세한 내용은 [Testing Applications - API](/testing/api.html)에서 알아보겠습니다.
88 |
--------------------------------------------------------------------------------
/docs/testing/jest-api.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Practical Jest
3 | ---
4 |
5 | ## Jest 문법 소개
6 |
7 | ```js
8 | test.only(); // watch 모드에서 해당 테스트 케이스만 집중할 수 있음
9 | test.skip(); // 테스트 검증시 해당 테스트 케이스는 스킵
10 | ```
11 |
12 | ## beforeAll()
13 |
14 | 해당 테스트 파일의 모든 테스트 케이스가 돌기 전에 실행할 로직을 넣는 API.
15 | 프로미스와 같은 비동기 코드의 동작까지 기다려주기 때문에 서버 실행이나 DB 설정 코드를 넣기에 좋다.
16 | `beforeEach()`와 다른점은 `beforeAll()`은 모든 테스트 케이스가 실행되기 전의 로직이고,
17 | `beforeEach()`는 각 테스트 케이스의 실행 전에 실행할 로직이다.
18 |
19 | ## expect.any()
20 |
21 | 특정 타입의 변수인지를 확인할 수 있는 API
22 |
23 | ```js
24 | expect(response.data.user).toContainEqual({
25 | name: expect.any(String),
26 | email: expect.any(String)
27 | })
28 | ```
29 |
30 | ## toHaveLength()
31 |
32 | 대상 변수의 길이를 체크하는 API
33 |
34 | ```js
35 | expect(response.data.user).toHaveLength(10)
36 | ```
--------------------------------------------------------------------------------
/docs/testing/overview.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Overview
3 | ---
4 |
5 | # 테스트 코드가 필요한 이유
6 |
7 | 개발자에게 테스트 코드가 필요한 이유는 아래 2가지에 소모되는 시간을 줄이기 위해서입니다.
8 |
9 | 1. 방금 구현한 기능이 잘 돌아가는지 확인하는 시간
10 | 2. 특정 기능을 변경했을 때 기존에 있던 다른 기능을 깨트리는지 확인하는 시간
11 |
12 | 애플리케이션이 커지면 커질수록 위 시나리오를 점검하는데 많은 시간이 소요됩니다. 특히 진행되고 있는 프로젝트에 투입되어서 남이 짠 코드를 변경한다고 했을 때, 코드를 변경하는 시간보다 그 코드가 다른 코드에 악영향을 주는지 분석하고 확인하는데 시간이 더 많이 들어가죠.
13 |
14 | 테스트 코드는 이러한 시간을 줄여주고 개발자의 자신감을 높여줍니다. 테스트 코드가 많으면 많을수록 더 애플리케이션의 안정성이 높아지죠.
15 |
16 | ## 무엇을 어떻게 테스트 할 것인가?
17 |
18 | 테스트 코드를 작성할 때 가장 중요한 것은 무엇을 어떻게 테스트 할 것인가 정하는 것입니다.
19 |
20 | 테스트 코드의 목적은 **구현된 코드의 흐름이나 로직을 확인하는 것이 아니라 사용자의 관점에서 버튼 클릭과 키 입력 등의 이벤트에 따라 UI가 올바르게 전개되는지 확인하는 것**입니다. 예를 들어 아래와 같은 코드가 있다고 해봅시다.
21 |
22 | ```html
23 |
24 |
25 |
26 |
{{ counter }}
27 |
28 |
29 |
30 |
44 | ```
45 |
46 | 위 코드는 add 버튼을 클릭했을 때 `counter` 값을 1씩 증가시키는 코드입니다. 이 코드가 잘 동작하는지 어떻게 확인할 수 있을까요? 아마 테스트 코드가 따로 없다고 가정한다면 대부분 버튼을 클릭해보고 `counter` 값이 증가하는지 확인할 겁니다.
47 |
48 | 여기서 테스트 코드를 적용한다고 했을 때 다음 2가지 시나리오를 점검할 수 있습니다.
49 |
50 | 1. add 버튼을 클릭했을 때 'counter' 값이 증가했는지 확인
51 | 2. add 버튼을 클릭했을 때 증가된 'counter' 값이 화면에 출력되는지 확인
52 |
53 | 위 2개 시나리오 중 사용자 관점에서 작성된 테스트 시나리오는 2번입니다. 1번은 단순히 코드 관점에서 메서드가 올바르게 동작하는지 확인하는 수준이죠.
54 |
55 | 이처럼 테스트 코드는 개발자가 구현한 로직이나 코드 결과를 검증하는 것이 아니라 특정 이벤트에 의해 변경될 UI를 검증할 수 있어야 합니다.
56 |
57 | ## 테스팅 도구
58 |
59 | 테스팅 도구는 요구 사항 변경에 따른 기능 추가 및 리팩토링을 할 때 소프트웨어의 안정성을 높여주는 도구입니다. 복잡한 웹 애플리케이션일수록, 그리고 같이 협업하는 팀원이 많을수록 테스트 케이스를 바탕으로 각자 기능을 구현하면 기능 변경에서 오는 에러를 미연에 방지할 수 있습니다.
60 |
61 | 시중에서 가장 많이 사용되는 테스팅 도구는 다음과 같습니다.
62 |
63 | - [Jest](https://jestjs.io/)
64 | - [Mocha](https://mochajs.org/)
65 | - [Jasmine](https://jasmine.github.io/)
66 | - [Enzyme](https://github.com/airbnb/enzyme)
67 |
68 | 다음 챕터에서는 Jest에 대해 알아보겠습니다.
69 |
--------------------------------------------------------------------------------
/docs/testing/snapshots.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Snapshots
3 | ---
4 |
5 | # Snapshot Testing
6 |
7 | 스냅샷(Snapshot Testing) 테스팅을 하기 위해 먼저 아래 환경을 구성합니다.
8 |
9 | ```bash
10 | npm install --save-dev jest-serializer-vue
11 | ```
12 |
13 | ```json
14 | // package.json
15 | {
16 | // ...
17 | "jest": {
18 | // ...
19 | // serializer for snapshots
20 | "snapshotSerializers": [
21 | "jest-serializer-vue"
22 | ]
23 | }
24 | }
25 | ```
26 |
27 | 그리고 코드는 다음과 같습니다.
28 |
29 | ```js
30 | import { mount } from '@vue/test-utils';
31 | import HelloWorld from '../HelloWorld.vue';
32 |
33 | describe('Hello World Component', () => {
34 | test('[Snapshot Testing] renders hello world message', () => {
35 | const { vm } = mount(HelloWorld);
36 | expect(vm.$el).toMatchSnapshot();
37 | });
38 | });
39 | ```
40 |
41 | 위 코드를 실행할 때 주의 할점은 명령어를 `npm t -u`로 해야 한다는 점입니다.
--------------------------------------------------------------------------------
/docs/textbook.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | ---
4 |
5 | # Cracking Vue.js
6 |
7 | 이 사이트는 Vue.js로 웹 서비스를 개발할 때 필요한 지식을 모아놓은 사이트입니다.
8 | [기본적인 자바스크립트 개념](/js/variable.html)부터 [Vue.js 기본](vue/instance.html), [실무 지식](reuse/slots.html)까지 모두 정리해 놓았습니다.
9 |
10 | ### 📖 입문
11 |
12 | - [자바스크립트 기본](/js/variable.html)
13 | - [ES6+](/es6+/const-let.html)
14 | - [Vue.js 기본](/vue/instance.html)
15 |
16 | ### 📖 중급
17 |
18 | - [Vue.js 실용 문법](/syntax/methods.html)
19 | - [재사용성을 높이는 개발 방법](/reuse/slots.html)
20 | - [레거시 프로젝트에서의 Vue.js 사용법](/legacy/jquery-to-vue.html)
21 | - [Vuex를 이용한 상태 관리](/vuex/concept.html)
22 |
23 | ### 📖 고급
24 |
25 | - [실무에서 알아야 하는 Vue.js 테크닉](/advanced/folder-structure.html)
26 | - [고급 컴포넌트 디자인 패턴](/design/pattern1.html)
27 | - [테스트 도구와 테스트 방법](/testing/overview.html)
28 | - [애플리케이션 배포 환경 변수 파일](/deploy/intro.html)
29 |
30 | ### 📖 기타
31 |
32 | - [TypeScript](/ts/intro.html)
33 | - [Nuxt](/nuxt/intro.html)
34 | - [VuePress](/vuepress/learning-note.html)
35 | - [NPM vs Yarn](/package-manager/npm-vs-yarn.html)
36 |
37 | ## 온라인 강의 학습 로드맵
38 |
39 |
--------------------------------------------------------------------------------
/docs/ts/getting-started.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getting Started
3 | ---
4 |
5 | # Vue.js에서 타입스크립트를 시작하는 방법
6 |
7 |
8 | ## shims-vue.d.ts
9 |
10 | ```js
11 | declare module "*.vue" {
12 | import Vue from 'vue';
13 | export default Vue;
14 | }
15 | ```
16 |
17 | Which basically means, "every time you import a module with the name *.vue (wildcards are supported), then don't actually do it - instead treat it as if it had these contents".
--------------------------------------------------------------------------------
/docs/ts/images/store-infer-error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/ts/images/store-infer-error.png
--------------------------------------------------------------------------------
/docs/ts/images/store-infer.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/ts/images/store-infer.gif
--------------------------------------------------------------------------------
/docs/ts/images/vue-ts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/ts/images/vue-ts.png
--------------------------------------------------------------------------------
/docs/ts/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Why TypeScript?
3 | ---
4 |
5 | # 왜 타입스크립트를 사용하는가?
6 |
7 | 타입이 없는 자바스크립트는 애플리케이션이 실행되었을 때만 에러를 검출할 수 있습니다. 따라서, 실행 시점에만 발생하는 잠재적인 오류에 대해서는 특별한 조치를 취하기가 어렵습니다. 이러한 문제점을 해결하고자 대두되는 기술이 타입스크립트입니다.
8 |
9 | ## TypeScript 맛보기
10 |
11 | 뷰에 타입스크립트를 연동해보기 전에 간단하게 타입스크립트의 코드를 살펴보겠습니다. 먼저 아래와 같이 타입스크립트를 전역으로 설치합니다.
12 |
13 | ```bash
14 | npm i typescript -g
15 | ```
16 |
17 | 이제 명령어 입력 창에서 `tsc` 라는 명령어를 실행할 수 있습니다. 이 명령어를 사용하면 타입스크립트로 작성된 코드를 브라우저에서 실행할 수 있는 자바스크립트 코드로 변환할 수 있게 되죠.
18 |
19 | 이제 타입스크립트 코드를 보겠습니다.
20 |
21 | ```ts
22 | // app.ts
23 | function sum(a: number, b: number) {
24 | return a + b;
25 | }
26 | ```
27 |
28 | 두 숫자의 합을 구하는 sum 함수에 타입스크립트를 적용한 코드입니다. 함수의 인자는 숫자(number)만 받을 수 있게 됩니다. 위의 코드를 아래와 같이 변환할 수 있습니다.
29 |
30 | ```bash
31 | tsc app.ts
32 | ```
33 |
34 | 실행 결과로 같은 이름의 js 파일인 `app.js`를 생성합니다.
35 |
36 | ## TypeScript의 장점
37 |
38 | 앞에서 살펴본 타입스크립트 코드의 장점은 위 코드를 브라우저에서 실행하기 전에 에러를 검출할 수 있다는 것입니다. 만약 코드가 아래와 같았다면 자바스크립트 코드로 변환할 때 어떤 일이 발생하였을까요?
39 |
40 | ```ts
41 | // app.ts
42 | function sum(a: number, b: number) {
43 | return a + b;
44 | }
45 |
46 | sum(10, [20]);
47 | ```
48 |
49 | 위 코드를 `tsc`로 변환하면 아래와 같은 에러가 발생됩니다.
50 |
51 | 
52 |
53 | 만약 타입스크립트를 쓰지 않고 그냥 브라우저에서 실행했다면 두 숫자의 합이 아니라 숫자와 배열을 더했을 겁니다. 따라서, 숫자의 합을 구하려고 만든 함수의 본질이 흐려지게 되죠.
54 |
55 | 실행 시점에서 유연하게 타입을 바꾸는 자바스크립트의 특징이 때로는 이와 같이 예기치 못한 동작과 에러를 발생시킬 수 있습니다. 하지만, 타입스크립트를 사용함으로써 이런 에러들을 미리 잡아낼 수 있죠.
56 |
57 | 이처럼 **브라우저에서 실행하고 잘못된 결과를 확인하기 전에 미리 컴파일(코드 변환) 시점에 타입 변환으로 인한 에러를 검출할 수 있는 것**이 타입스크립트의 장점입니다.
58 |
59 | ## 타입스크립트에 대해 더 자세히 알고 싶다면?
60 |
61 | - [캡틴판교의 타입스크립트 핸드북](https://joshua1988.github.io/ts/)
62 | - [캡틴판교의 쉽게 시작하는 타입스크립트 도서](https://www.yes24.com/Product/Goods/119410497)
63 | - [캡틴판교의 타입스크립트 입문 강의](https://www.inflearn.com/course/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%85%EB%AC%B8?inst=f1ae9299&utm_source=blog&utm_medium=githubio&utm_campaign=captianpangyo&utm_term=banner)
--------------------------------------------------------------------------------
/docs/ts/pdecorator.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Vue Property Decorator
3 | ---
4 |
5 | # Vue Property Decorator
6 |
7 | 앞에서 살펴본 뷰 클래스 컴포넌트([vue-class-component](https://github.com/vuejs/vue-class-component)) 라이브러리 이외에도 타입스크립트로 뷰 컴포넌트를 작성할 때 유용한 라이브러리가 있습니다. 뷰 프로퍼티 데코레이터([Vue Property Decorator](https://github.com/kaorun343/vue-property-decorator)) 라이브러리입니다.
8 |
9 | :::tip
10 | 참고로 뷰 프로퍼티 데코레이터 라이브러리는 뷰 클래스 컴포넌트를 기반으로 제작되었습니다.
11 | :::
12 |
13 | ## 맛보기 코드
14 |
15 | Vue Property Decorator가 적용된 코드 예시입니다.
16 |
17 | ```html {9,14}
18 |
19 |
20 |
21 |
{{ msg }}
22 |
23 |
24 |
25 |
34 | ```
35 |
36 | 위 컴포넌트는 상위 컴포넌트에서 `msg`라는 props 속성을 내려 받은 하위 컴포넌트 코드입니다. 임포트한 Prop 데코레이터로 내려 받은 props 속성을 정의해주었습니다. 만약 프롭스 속성에 [Props Validation](https://vuejs.org/v2/guide/components-props.html#Prop-Validation)을 주고 싶다면 아래와 같이 정의하면 됩니다.
37 |
38 | ```js
39 | @Prop(String) private msg!: string;
40 | @Prop([String, Number]) private msg!: string;
41 | @Prop({ default: 'hi' }) private msg!: string;
42 | ```
--------------------------------------------------------------------------------
/docs/ts/refs.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ref
3 | ---
4 |
5 | # ref 타이핑
6 |
7 | 뷰 타입스크립트 프로젝트에서 `ref` 속성의 타입을 정의하는 방법에 대해 알아봅니다.
8 |
9 | ## ref 속성이란?
10 |
11 | ref 속성은 뷰에서 특정 DOM이나 컴포넌트의 정보를 접근하기 위해 사용하는 속성입니다.
12 |
13 | ```html
14 |
15 |
16 |
23 | ```
24 |
25 | ## ref 속성 타입 정의 방법
26 |
27 | 뷰 컴포넌트 파일에서 `ref` 속성을 사용할 때는 아래와 같이 타입을 정의할 수 있습니다.
28 |
29 | ```html{3,10-12,14}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
47 | ```
48 |
49 | ## 반복적인 타입 코드 줄이는 방법
50 |
51 | 컴포넌트마다 매번 위와 같은 형태의 타입을 정의하기가 번거롭다면 코드를 좀 더 줄일 수 있는 제네릭 타입을 선언합니다.
52 |
53 | ```ts
54 | // src/types.ts
55 | type MyVue = VueConstructor;
56 | type MyVueRefs = VueConstructor;
57 |
58 | // App.vue
59 | export default (Vue as MyVueRefs<{ my: HTMLDivElement }>).extend({
60 | mounted() {
61 | this.$refs.my; // HTMLDivElement
62 | }
63 | });
64 | ```
--------------------------------------------------------------------------------
/docs/vite/images/import-error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/docs/vite/images/import-error.png
--------------------------------------------------------------------------------
/docs/vite/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | ---
4 |
5 | # Vite
6 |
7 | [비트(Vite)](https://vitejs.dev/)는 기존의 프런트엔드 개발 경험을 향상시켜줄 새로운 프런트엔드 툴입니다. Vue 창시자 에반 유가 만들었으며 현재 Vue, React, Svelte 등의 주요 프레임워크 커뮤니티에서 주목하고 있는 도구입니다. 기존의 프런트엔드 빌드 도구들과 어떠한 차이점이 있는지 어떤 경험을 우리에게 제공해주는지 알아보겠습니다.
8 |
9 | ## Vite란?
10 |
11 | 비트는 [자바스크립트 네이티브 모듈](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)을 기반으로 한 데브 서버입니다. 이미 현대 프런트엔드 개발 생태계는 웹팩을 중심으로 개발 환경과 배포 시스템이 구축되어 있습니다. 그런데 왜 비트와 같은 도구들이 나왔을까요? 그 이유는 웹팩을 사용할 때보다 훨씬 더 빠르게 개발하고 배포할 수 있기 때문입니다. 왜 빠른지 이해하기 위해서는 먼저 번들링과 자바스크립트 네이티브 모듈을 이해해야 합니다.
12 |
13 | ## 번들링
14 |
15 | [웹팩(Webpack)](https://joshua1988.github.io/webpack-guide), [롤업(Rollup)](https://rollupjs.org/guide/en/), [파셀(Parcel)](https://en.parceljs.org/)과 같은 모듈 번들러가 나왔던 이유는 자바스크립트의 모듈화와 깊은 관계가 있습니다. 위에서 자바스크립트 네이티브 모듈이라고 언급했던 ESM(ECMAScript Modules)이 등장하기 전까지는 자바스크립트 언어 레벨에서의 모듈화 방식은 없었습니다. [require.js](https://requirejs.org/) 와 같은 모듈 로더나 [IIFE(Immediately Invoked Function Expression)](https://developer.mozilla.org/en-US/docs/Glossary/IIFE)를 사용하지 않으면 모듈화가 불가능했죠. 이렇게 모듈화를 위한 커뮤니티 레벨의 라이브러리를 사용하다가 마침내 자바스크립트 언어에 모듈화 문법(import, export)이 들어오게 됩니다.
16 |
17 | 이 모듈화 문법을 이용하여 여러 개의 파일을 하나로 합쳐주거나 의미 있는 단위로 묶어 주는 것을 번들링이라고 하는데 현재 프런트엔드 개발 생태계에서는 모듈 번들러로 대부분 웹팩을 사용하고 있습니다.
18 |
19 | ## ESM(자바스크립트 네이티브 모듈)
20 |
21 | ESM은 모듈화 문법인 `import`, `export`를 별도의 도구 없이 브라우저 자체에서 소화해 낼 수 있는 모듈 방식을 의미합니다. 만약 아래와 같은 코드를 웹팩과 같은 번들러 없이 브라우저에서 실행하면 에러가 발생합니다.
22 |
23 | ```js
24 | // app.js
25 | import { sum } from './math.js';
26 |
27 | console.log(sum(10, 20));
28 | ```
29 |
30 | ```html
31 |
32 | ```
33 |
34 | 
35 |
36 | 기존의 브라우저에서는 `import`와 `export`를 해석할 수 있는 능력이 없었습니다. 하지만, 이제는 `script` 태그에 아래와 같이 `type="module"` 속성을 추가하면 정상 동작하는 것을 볼 수 있습니다.
37 |
38 | ```html
39 |
40 | ```
41 |
42 | 브라우저에서 `import`와 `export`를 소화할 수 있는 능력이 바로 ESM입니다.
43 |
44 | ## Vite 특징
45 |
46 | 비트는 로컬에서 개발할 때 번들링을 하지 않고 ESM 방식을 사용하기 때문에 로컬 서버 구동 속도가 매우 빠릅니다. 500개 정도 되는 모듈을 갖고 있는 웹 서비스를 [웹팩 데브 서버](https://joshua1988.github.io/webpack-guide/devtools/webpack-dev-server.html)와 비트로 비교해 본다면 실행 시간이 20 ~ 30배 이상 차이가 납니다. 웹팩 데브 서버는 처음 로컬 서버를 시작할 때 관련 있는 모듈들을 번들링 해서 메모리에 적재하는 시간이 필요하기 때문에 당연히 어느 정도의 시간이 필요합니다. 반면 비트는 번들링을 하지 않고 바로 서버를 실행하기 때문에 명령어를 실행함과 동시에 서버가 바로 구동됩니다.
47 |
48 | 프로덕션을 위한 빌드를 실행할 때도 비트 자체에서 제공해 주는 빌드 옵션이 많습니다. 비트의 빌드 결과물 역시 기본적으로 번들링은 하지 않고 로컬 개발 서버의 ESM 방식을 사용하고 있습니다. 하지만, 서비스 규모가 너무 큰 경우에는 선택적으로 번들링을 하는게 더 이득일 수 있어 번들링 도구로 Rollup을 사용할 수 있습니다. 기본적으로는 비트 자체에서도 충분히 프로덕션으로 배포할 수 있는 수준의 최적화를 진행해 주지만 별도의 번들링과 추가적인 커스텀 빌드 작업을 진행하고 싶은 경우에는 아래와 같이 롤업 번들러를 끼워서 사용할 수 있습니다.
49 |
50 | ```js
51 | // vite.config.js
52 | module.exports = defineConfig({
53 | build: {
54 | rollupOptions: {
55 | // https://rollupjs.org/guide/en/#big-list-of-options
56 | }
57 | }
58 | })
59 | ```
60 |
61 | ## Vite 시작하기
62 |
63 | 비트 프로젝트를 생성하기 위해서 아래 명령어를 입력합니다.
64 |
65 | ```sh
66 | npm init vite@latest my-vite
67 | ```
68 |
69 | 리액트, 뷰, 스벨트 등 원하는 템플릿을 선택하여 프로젝트를 생성합니다.
70 |
71 | :::tip
72 | 비트는 [Node.js 버전 12 이상](https://nodejs.org/en/)에서 정상적으로 동작합니다.
73 | :::
--------------------------------------------------------------------------------
/docs/vue/cli.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: CLI
3 | ---
4 |
5 | # 뷰 CLI
6 |
7 | 뷰 CLI는 뷰로 빠르게 프로젝트를 구성하고 프로토 타이핑을 하고 싶을 때 사용하는 CLI 도구입니다.
8 |
9 | ::: tip
10 | 최신 버전은 5.x이며 책에서는 2.9 버전을 기준으로 설명하였습니다.
11 | :::
12 |
13 | ## 뷰 CLI 설치
14 |
15 | 뷰 CLI를 설치하기 위해서는 기본적으로 Node.js를 시스템에 설치해놔야 합니다. Node.js를 설치하고 나면 사용할 수 있는 [NPM(Node Package Manager)](https://joshua1988.github.io/webpack-guide/build/node-npm.html#npm)을 이용하여 뷰 CLI를 설치합니다.
16 |
17 | ```bash
18 | # 버전 5.x
19 | npm install -g @vue/cli
20 |
21 | # 버전 2.9
22 | npm install vue-cli
23 | ```
24 |
25 | ## 뷰 CLI로 프로젝트 생성하기
26 |
27 | 프로젝트를 생성하는 명령어는 아래와 같습니다.
28 |
29 | ```bash
30 | # 버전 5.x
31 | vue create helloworld
32 | ```
33 |
34 | ```bash
35 | # 버전 2.9
36 | vue init webpack-simple 파일 경로
37 | ```
38 |
39 | ## 뷰 CLI 5.x과 CLI 2.9와 차이점
40 |
41 | 기존 뷰 CLI 2.9의 프로젝트 생성 방식은 깃헙 리포지토리에 등록된 폴더와 파일을 그대로 다운로드 받는 것이었습니다. 그에 반해 뷰 CLI 5.x는 뷰 플러그인을 이용하여 필요한 기능들을 추가해 나가는 형식입니다.
42 |
43 | 그리고 차이점은 크게 아래와 같이 정리됩니다.
44 |
45 | | | Vue CLI 5.x | Vue CLI 2.9 |
46 | |:-----------:|----------------------------------------------------------------|-----------------------------------|
47 | | **언어** | ES6+ 기준 | ES5 기준 (ES6+ 선택 가능) |
48 | | **웹팩** | 웹팩 설정 파일이 숨겨져 있음 [(필요한 설정은 추가하는 형식)](https://cli.vuejs.org/guide/webpack.html#working-with-webpack) | 웹팩 설정 파일에서 바로 수정 가능 |
49 | | **GUI** | GUI 툴 제공 (vue ui) | X |
50 | | **구성** | 뷰 플러그인 형식 | [프로젝트 템플릿](https://github.com/vuejs-templates/webpack-simple)을 깃헙에서 다운로드 |
51 | | **명령어** | vue create '프로젝트 이름' | vue init '프로젝트 템플릿 이름' '프로젝트 폴더 경로' |
--------------------------------------------------------------------------------
/docs/vue/components-communication.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Components Communication
3 | ---
4 |
5 | # 컴포넌트 통신 방식
6 |
7 | 뷰 컴포넌트는 각각 고유한 데이터 유효 범위를 갖습니다. 따라서, 컴포넌트 간에 데이터를 주고 받기 위해선 아래와 같은 규칙을 따라야 합니다.
8 |
9 | 
10 |
11 | - 상위에서 하위로는 데이터를 내려줌, [프롭스 속성](/vue/props.html)
12 | - 하위에서 상위로는 이벤트를 올려줌, [이벤트 발생](/vue/event-emit.html)
13 |
14 | 각 컴포넌트 통신 방법과 관련된 자세한 설명은 해당 챕터를 참고하세요.
--------------------------------------------------------------------------------
/docs/vue/components.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Components
3 | ---
4 |
5 | # 뷰 컴포넌트
6 |
7 | 컴포넌트는 화면의 영역을 구분하여 개발할 수 있는 뷰의 기능입니다. 컴포넌트 기반으로 화면을 개발하게 되면 코드의 재사용성이 올라가고 빠르게 화면을 제작할 수 있습니다.
8 |
9 | 
10 |
11 | ## 컴포넌트 생성 코드 형식
12 |
13 | 컴포넌트를 생성하는 코드 형식은 아래와 같습니다.
14 |
15 |
16 |
17 | ```js
18 | Vue.component('컴포넌트 이름', {
19 | // 컴포넌트 내용
20 | });
21 | ```
22 |
23 |
24 |
25 | ```js
26 | // 인스턴스 생성
27 | var app = Vue.createApp();
28 |
29 | app.component('컴포넌트 이름', {
30 | // 컴포넌트 내용
31 | });
32 | ```
33 |
34 |
35 |
36 | ## 컴포넌트 생성 후 표시하기
37 |
38 | 위 코드 형식을 참고하여 간단한 앱 헤더 컴포넌트를 만들어보겠습니다.
39 |
40 | ```js
41 | Vue.component('app-header', {
42 | template: '
Header Component
'
43 | });
44 | ```
45 |
46 | 위에서 등록한 컴포넌트를 화면에서 표시하려면 아래와 같이 컴포넌트 태그(컴포넌트 이름)를 추가합니다.
47 |
48 | ```html
49 |
50 |
51 |
52 | ```
53 |
54 | div 태그에 뷰 인스턴스가 생성되어 있다는 가정하에 위 템플릿 코드는 결과적으로 아래와 같이 표시됩니다.
55 |
56 | ```html
57 |
58 |
Header Component
59 |
60 | ```
61 |
62 | ## 컴포넌트 등록 방법 2가지
63 |
64 | 컴포넌트를 등록하는 방법은 크게 2가지가 있습니다. 앞에서 살펴본 방식은 전역 컴포넌트를 등록하는 방법입니다.
65 |
66 |
67 |
68 | ```js
69 | // 전역 컴포넌트 등록
70 | Vue.component('app-header', {
71 | template: '
'
84 | });
85 | ```
86 |
87 |
88 |
89 | 또 다른 방법은 지역 컴포넌트로 등록하는 방법이 있습니다. 앞에서 사용한 컴포넌트 내용을 가지고 그대로 지역 컴포넌트로 등록하면 아래와 같습니다.
90 |
91 |
92 |
93 | ```js
94 | var appHeader = {
95 | template: '
'
111 | }
112 |
113 | Vue.createApp({
114 | // 지역 컴포넌트 등록
115 | components: {
116 | 'app-header': appHeader
117 | }
118 | })
119 | ```
120 |
121 |
--------------------------------------------------------------------------------
/docs/vue/event-emit.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Event Emit
3 | ---
4 |
5 | # 이벤트 발생
6 |
7 | 이벤트 발생은 컴포넌트의 통신 방법 중 하위 컴포넌트에서 상위 컴포넌트로 통신하는 방식입니다.
8 |
9 | ## 이벤트 발생 코드 형식
10 |
11 | 하위 컴포넌트의 메서드나 라이프사이클 훅과 같은 곳에 아래와 같이 코드를 추가합니다.
12 |
13 | ```js
14 | // 하위 컴포넌트의 내용
15 | this.$emit('이벤트 명');
16 | ```
17 |
18 | 그리고 나서 해당 이벤트를 수신하기 위해 상위 컴포넌트의 템플릿에 아래와 같이 구현합니다.
19 |
20 | ```html
21 |
22 |
84 | ```
85 |
86 |
--------------------------------------------------------------------------------
/docs/vue/sfc.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Single File Components
3 | ---
4 |
5 | # 싱글 파일 컴포넌트
6 |
7 | 싱글 파일 컴포넌트는 화면의 특정 영역에 대한 HTML, CSS, JS 코드를 한 파일에서 관리하는 방법입니다. 뷰 CLI로 프로젝트를 생성하고 나면 App.vue라는 파일을 확인할 수 있습니다. 이처럼 vue 확장자를 가진 파일을 모두 싱글 파일 컴포넌트라고 합니다.
8 |
9 | 코드를 살펴보겠습니다.
10 |
11 | ```html
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
24 | ```
25 |
26 | ## 싱글 파일 컴포넌트의 동작 원리
27 |
28 | 싱글 파일 컴포넌트는 뷰 로더에 의해 HTML, CSS, JS와 같은 웹 자원으로 분리되어 실행됩니다. [뷰 로더](https://vue-loader.vuejs.org/guide/)는 웹팩의 로더 종류 중 하나이고 뷰 CLI로 프로젝트를 생성하면 기본적으로 설정이 되어 있습니다.
29 |
--------------------------------------------------------------------------------
/docs/vue/template.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Template
3 | ---
4 |
5 | # 뷰의 템플릿 문법
6 |
7 | 뷰의 템플릿 문법이란 뷰로 화면을 조작하는 방법을 의미합니다. 템플릿 문법은 크게 데이터 바인딩과 디렉티브로 나뉩니다.
8 |
9 | ## 데이터 바인딩
10 |
11 | 데이터 바인딩은 뷰 인스턴스에서 정의한 속성들을 화면에 표시하는 방법입니다. 가장 기본적인 데이터 바인딩 방식은 콧수염 괄호(Mustache Tag)입니다.
12 |
13 | ```html
14 |
{{ message }}
15 | ```
16 |
17 |
18 |
19 | ```js
20 | new Vue({
21 | data: {
22 | message: 'Hello Vue.js'
23 | }
24 | })
25 | ```
26 |
27 |
28 |
29 | ```js
30 | Vue.createApp({
31 | data() {
32 | return {
33 | message: 'Hello Vue.js'
34 | }
35 | }
36 | })
37 | ```
38 |
39 |
40 |
41 | div 태그에 콧수염 괄호를 이용해 뷰 인스턴스의 `message` 속성을 연결했습니다. 코드를 실행하면 화면에 Hello Vue.js라는 코드가 출력됩니다.
42 |
43 | ## 디렉티브
44 |
45 | 디렉티브는 뷰로 화면의 요소를 더 쉽게 조작하기 위한 문법입니다. 화면 조작에서 자주 사용되는 방식들을 모아 디렉티브 형태로 제공하고 있습니다. 예를 들어 아래와 같이 특정 속성 값에 따라 화면의 영역을 표시하거나 표시하지 않을 수 있습니다.
46 |
47 | ```html
48 |
83 | ```
84 |
85 |
86 |
87 | ```js
88 | new Vue({
89 | data: {
90 | items: ['shirts', 'jeans', 'hats']
91 | }
92 | })
93 | ```
94 |
95 |
96 |
97 | ```js
98 | Vue.createApp({
99 | data() {
100 | return {
101 | items: ['shirts', 'jeans', 'hats']
102 | }
103 | }
104 | })
105 | ```
106 |
107 |
108 |
109 | v-for 디렉티브를 활용하면 데이터 속성의 개수만큼 화면의 요소를 반복하여 출력할 수 있습니다. 목록을 표시해야 할 때 유용하게 사용할 수 있는 기능입니다.
110 |
111 | 이외에도 자주 사용되는 디렉티브는 다음과 같습니다.
112 |
113 | - v-bind
114 | - v-on
115 | - v-model
--------------------------------------------------------------------------------
/docs/vuepress/learning-note.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Tutorials
3 | ---
4 |
5 | # VuePress 사이트 배포하기 절차
6 |
7 | VuePress로 그날 그날 배운 내용을 정리하기 위해 **학습 노트** 사이트를 제작합니다.
8 |
9 | ## 사이트 배포 하기 절차
10 |
11 | 1. [깃헙](https://github.com/) 계정 생성
12 | 2. 깃헙 리포지토리 생성
13 | 3. 생성한 리포지토리를 클론
14 |
15 | ```bash
16 | # 학습 노트를 관리할 폴더의 위치에서 아래 명령어 입력
17 | git clone '생성한 리포지토리 클론 주소'
18 | ```
19 |
20 | 4. 클론된 프로젝트 위치로 이동
21 |
22 | ```bash
23 | cd '클론해서 생성된 폴더 이름'
24 | ```
25 |
26 | 5. 프로젝트에서 `docs` 폴더 생성
27 | 6. 프로젝트에서 아래와 같은 명령어를 순서대로 입력
28 |
29 | ```bash
30 | npm init -y
31 | npm install -D vuepress
32 | ```
33 |
34 | ::: tip
35 | 만약 permission 관련된 오류가 날 경우에는 `npm install -D vuepress` 대신에 `sudo npm install -D vuepress` 입력하세요.
36 | :::
37 |
38 | 7. `package.json` 파일의 내용을 아래와 같이 수정
39 |
40 | ```json
41 | {
42 | ...
43 | "scripts": {
44 | "doc": "vuepress dev docs",
45 | "build": "vuepress build docs"
46 | },
47 | ...
48 | }
49 | ```
50 |
51 | 8. `docs` 폴더 밑에 `README.md` 파일을 생성하고 아래의 내용을 추가
52 |
53 | ```md
54 | # Vue.js 정복 캠프 학습 노트!!
55 | ```
56 |
57 | 9. **클론된 프로젝트 폴더** 밑에 `deploy.sh` 파일을 추가하고 아래 내용 삽입
58 |
59 | ```sh
60 | #!/usr/bin/env sh
61 |
62 | # abort on errors
63 | set -e
64 |
65 | # build
66 | npm run build
67 |
68 | # navigate into the build output directory
69 | cd docs/.vuepress/dist
70 |
71 | # if you are deploying to a custom domain
72 | # echo 'www.example.com' > CNAME
73 |
74 | git init
75 | git add -A
76 | git commit -m 'deploy with vuepress'
77 |
78 | # if you are deploying to https://.github.io
79 | # git push -f git@github.com:/.github.io.git master
80 |
81 | # if you are deploying to https://.github.io/
82 | git push -f <뷰 프레스를 위해 생성한 깃헙 리포지토리의 클론 주소> master:gh-pages
83 |
84 | cd -
85 | ```
86 |
87 | ::: danger
88 | 위에서 `<뷰 프레스를 위해 생성한 깃헙 리포지토리의 클론 주소>`는 꼭 리포지토리의 주소로 변경해주셔야 합니다.
89 | :::
90 |
91 | 10. 프로젝트 폴더의 위치에서 `sh deploy.sh`를 실행하여 사이트 배포
92 |
93 | ::: tip
94 | `sh`라는 명령어는 윈도우 기본 명령어 프롬프트 창에서 먹지 않습니다. 따라서 [cmder](https://cmder.net/)이라는 콘솔 프로그램을 설치해서 실행하거나 git bash와 같은 별도의 콘솔창을 사용하시면 됩니다 :)
95 | :::
96 |
97 | 11. 브라우저에 `https://.github.io/` 입력 후 사이트 확인 (보통 5~10분 소요)
--------------------------------------------------------------------------------
/docs/vuex/actions.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Actions
3 | ---
4 |
5 | # actions
6 |
7 | 액션(actions)은 뮤테이션 중에서 비동기 처리 로직들을 정의하는 속성입니다. 동기 처리는 뮤테이션, 비동기 처리는 액션으로 이해하시면 됩니다. 혹시 자바스크립트의 비동기 처리에 대해서 잘 모르시는 분들은 [이 글](https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/)을 참고하세요.
8 |
9 | ## actions 선언
10 |
11 | 액션을 정의하는 코드입니다. 앞 [뮤테이션 챕터](./mutations.html#mutations-선언)에서 살펴봤던 코드를 살짝 바꿔봤습니다. 뮤테이션 코드에서는 컴포넌트 단에서 `commit('reverseMessage')` API를 호출하여 state 속성에 정의되어 있던 상태 값을 변환했습니다.
12 |
13 | ```js
14 | new Vuex.Store({
15 | state: {
16 | message: ''
17 | },
18 | mutations: {
19 | reverseMessage(state, data) {
20 | state.message = data.split('').reverse().join('');
21 | }
22 | },
23 | actions: {
24 | fetchMessage(context) {
25 | axios.get(url).then(function(response) {
26 | context.commit('reverseMessage', response.message);
27 | });
28 | }
29 | }
30 | })
31 | ```
32 |
33 | 이번에는 컴포넌트 단에서 액션을 호출하고 나면 `fetchMessage()` 라는 액션 메서드가 동작합니다. `fetchMessage()` 메서드는 HTTP 통신을 처리하기 때문에 비동기 코드가 되고, GET 요청의 응답으로 온 값을 뮤테이션의 인자로 넘겨서 역순으로 변환한 다음에 `message` 상태 값에 담아줍니다.
34 |
35 | ## actions 호출
36 |
37 | 액션을 컴포넌트에서 호출하는 방법은 아래와 같습니다.
38 |
39 | ```js
40 | new Vue({
41 | methods: {
42 | getMessage() {
43 | this.$store.dispatch('fetchMessage');
44 | }
45 | }
46 | })
47 | ```
48 |
49 | `getMessage()` 메서드를 호출하면 액션의 `fetchMessage()` 속성 함수가 실행됩니다.
--------------------------------------------------------------------------------
/docs/vuex/concept.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Concept
3 | ---
4 |
5 | # 상태 관리 소개
6 |
7 | 상태 관리란 현대 프런트엔드 프레임워크에서 모두 사용하는 개발 패턴입니다. 뷰에서는 뷰엑스(Vuex)라는 상태 관리 패턴을 사용합니다. 상태 관리가 필요한 이유는 컴포넌트의 숫자가 많아졌을 때 컴포넌트 간의 데이터 전달이나 관리가 어렵기 때문입니다. 데이터 전달을 더 명시적이고 효율적으로 하기 위한 방법이 상태 관리입니다.
8 |
9 | ## 뷰엑스 소개
10 |
11 | 뷰엑스란 뷰의 상태 관리 패턴이자 라이브러리입니다. 아래는 뷰엑스의 개념을 단순하게 도식화한 그림입니다.
12 |
13 | 
14 |
15 | 화면(View) -> 화면에서의 이벤트 발생(Actions) -> 데이터 변경(State)의 단방향 데이터 흐름이 특징입니다.
16 |
17 | ## 뷰엑스 주요 속성
18 |
19 | 뷰엑스의 주요 속성은 다음 4개입니다.
20 |
21 | - state
22 | - getters
23 | - mutations
24 | - actions
25 |
26 | 각 속성에 대한 자세한 설명은 해당 챕터에서 확인하겠습니다.
27 |
28 | ## 뷰엑스 설치
29 |
30 | 뷰엑스를 프로젝트에 설치하기 위해서 아래의 방법을 따릅니다.
31 |
32 | ### CDN 방식
33 |
34 | ```html
35 |
36 | ```
37 |
38 | ### NPM 방식
39 |
40 | ```bash
41 | npm install vuex --save
42 | ```
43 |
44 | ## 뷰엑스 등록
45 |
46 | 뷰엑스를 등록하기 위해서는 뷰 라우터와 마찬가지로 뷰 스토어를 하나 생성해야 합니다.
47 |
48 |
49 |
50 | ```js
51 | // store.js
52 | import Vue from 'vue';
53 | import Vuex from 'vuex';
54 |
55 | Vue.use(Vuex);
56 |
57 | export const store = new Vuex.Store({
58 | // ..
59 | });
60 | ```
61 |
62 |
63 |
64 | ```js
65 | // store.js
66 | import { createStore } from 'vuex'
67 |
68 | export const store = createStore({
69 | // ..
70 | });
71 | ```
72 |
73 |
74 |
75 |
76 |
77 | 뷰 스토어를 하나 생성하고 나서 ES6 import/export 문법으로 main.js 파일의 인스턴스에 주입합니다.
78 |
79 |
80 |
81 | ```js
82 | // main.js
83 | import Vue from 'vue';
84 | import { store } from './store.js';
85 |
86 | new Vue({
87 | store: store
88 | });
89 | ```
90 |
91 |
92 |
93 | ```js
94 | // main.js
95 | import { createApp } from 'vue';
96 | import Vue from 'vue';
97 | import { store } from "./store.js";
98 |
99 | createApp(App).use(store).mount('#app');
100 | ```
101 |
102 |
103 |
104 | Vue 3에서 Vuex를 사용하는 경우에는 인스턴스에 주입하는게 아니라 인스턴스에 체이닝 하는 형태로 설정합니다. 자세한 내용은 위 코드를 참고하세요.
105 |
106 | ## 뷰엑스 구조도
107 |
108 | 뷰엑스의 전체 흐름을 나타낸 그림입니다.
109 |
110 | 
111 |
112 | 데이터의 흐름은 Actions -> Mutations -> State 순서임을 알 수 있습니다.
--------------------------------------------------------------------------------
/docs/vuex/getters.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getters
3 | ---
4 |
5 | # getters
6 |
7 | getters 속성은 computed 속성과 같은 역할을 합니다. 상태(state) 값이 변경되었을 때 변화에 따른 차이를 자동으로 반영하여 값을 계산해줍니다.
8 |
9 | ## getters 선언
10 |
11 | getters 속성은 다음과 같이 정의합니다.
12 |
13 | ```js
14 | new Vuex.Store({
15 | state: {
16 | message: 'Hello Vue.js'
17 | },
18 | getters: {
19 | reverseMessage(state) {
20 | return state.message.split('').reverse().join('');
21 | }
22 | }
23 | })
24 | ```
25 |
26 | 위 코드는 `reverseMessage` 라는 getters 속성을 선언하여 상태 값 message의 문자열 순서를 거꾸로 뒤집는 코드입니다.
27 |
28 | ## getters 접근
29 |
30 | getters 속성은 컴포넌트에서 아래와 같이 접근합니다.
31 |
32 | ```html
33 |
{{ $store.getters.reverseMessage }}
34 | ```
35 |
36 | 코드를 실행하면 화면에 sj.euV olleH가 출력됩니다.
37 |
38 | ::: tip
39 | 아직 감이 안잡히신다면 computed의 [예제 코드](../syntax/computed.html)와 위의 코드를 비교해보세요.
40 | :::
--------------------------------------------------------------------------------
/docs/vuex/helper.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Helpers
3 | ---
4 |
5 | # 헬퍼 함수
6 |
7 | 헬퍼 함수는 뷰엑스의 주요 속성들을 컴포넌트에서 더 편하게 쓸 수 있도록 도와주는 API입니다. 얼마나 더 편해지는지 일반적인 사용 방식과 헬퍼 함수의 사용 방식을 비교해보겠습니다.
8 |
9 | ## 일반적인 getters 접근 방법
10 |
11 | reverseMessage라는 getters 속성을 컴포넌트에서 접근하려면 아래와 같이 구현해야 합니다.
12 |
13 | ```html
14 |
{{ this.$store.getters.reverseMessage }}
15 | ```
16 |
17 | ```js
18 | export default {
19 | computed: {
20 | reverseMessage() {
21 | return this.$store.getters.reverseMessage;
22 | }
23 | }
24 | }
25 | ```
26 |
27 | ## 헬퍼 함수의 사용 방식
28 |
29 | 이를 좀 더 편하게 접근하려면 아래와 같이 구현할 수 있습니다.
30 |
31 | ```html
32 |
{{ reverseMessage }}
33 | ```
34 |
35 | ```js
36 | import { mapGetters } from 'vuex';
37 |
38 | export default {
39 | computed: {
40 | ...mapGetters(['reverseMessage'])
41 | }
42 | }
43 | ```
44 |
45 | 위의 `...`는 ES6의 Spread Operator를 의미합니다. 헬퍼 함수를 사용할 때는 무조건 함께 사용하는 것이 좋습니다. `mapGetters()`는 스토어의 getters 속성을 `this.$store.getters....` 이런 식으로 접근하지 않
46 | 고 바로 접근할 수 있게 해줍니다.
47 |
48 | ## 헬퍼 함수의 종류
49 |
50 | 뷰엑스의 기술 요소 전부 헬퍼 함수를 갖고 있습니다. 사용법은 다음과 같습니다.
51 |
52 | ```js
53 | import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
54 |
55 | export default {
56 | computed: {
57 | ...mapState(),
58 | ...mapGetters()
59 | },
60 | methods: {
61 | ...mapMutations(),
62 | ...mapActions(),
63 | }
64 | }
65 | ```
66 |
67 | mapState와 mapGetters는 computed 속성에 주로 사용하고, mapMutations와 mapActions는 메서드에 주로 연결됩니다.
--------------------------------------------------------------------------------
/docs/vuex/modules.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Modules
3 | ---
4 |
5 | # 스토어 모듈화
6 |
7 | 애플리케이션에 스토어를 적용해서 사용하다가 보면 금방 스토어의 덩어리가 커집니다. 그럴때 데이터(상태)의 성격별로 스토어를 모듈화 해주면 훨씬 관리하기가 수월합니다.
8 |
9 | ## 스토어 모듈화 코드 형식
10 |
11 | 스토어를 모듈화 하는 코드는 다음과 같습니다.
12 |
13 | ```js
14 | new Vuex.Store({
15 | modules: {
16 | 모듈 명: 모듈의 내용
17 | }
18 | })
19 | ```
20 |
21 | ## 스토어 모듈화 예시
22 |
23 | 어느 애플리케이션이든지 인증은 거의 필수로 존재합니다. 그러면 사용자의 인증 정보(auth)와 상품 정보(product)를 각각 모듈로 분할해보겠습니다. 스토어의 코드는 다음과 같습니다.
24 |
25 | ```js
26 | // store.js
27 | import auth from './modules/auth/index.js';
28 | import product from './modules/product/index.js';
29 |
30 | new Vuex.Store({
31 | modules: {
32 | auth: auth,
33 | product: product
34 | }
35 | })
36 | ```
37 |
38 | 위에서 임포트하는 auth와 product 모듈의 내용은 아래와 같습니다.
39 |
40 | ```js
41 | // auth.js, product.js
42 | import state from './state.js';
43 | import getters from './getters.js';
44 | import mutations from './mutations.js';
45 | import actions from './actions.js';
46 |
47 | export default {
48 | namespaced: true,
49 | state: state,
50 | getters: getters,
51 | mutations: mutations,
52 | actions: actions
53 | }
54 | ```
55 |
56 | 뷰엑스 모듈화의 핵심은 ES6의 modules 구문을 잘 이해하는 것입니다. 그리고 각 모듈들의 속성명이 유일하도록 `namespaced: true`를 꼭 추가해줘야 합니다.
--------------------------------------------------------------------------------
/docs/vuex/mutations.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Mutations
3 | ---
4 |
5 | # mutations
6 |
7 | 뮤테이션(mutations)은 뷰엑스에서 상태 값을 변경하는 유일한 방법입니다. 상태는 항상 뮤테이션으로 변경됩니다.
8 |
9 | ## mutations 선언
10 |
11 | 뮤테이션을 선언하는 코드입니다.
12 |
13 | ```js
14 | new Vuex.Store({
15 | state: {
16 | message: 'Hello Vue.js'
17 | },
18 | mutations: {
19 | reverseMessage(state) {
20 | state.message = state.message.split('').reverse().join('');
21 | }
22 | }
23 | })
24 | ```
25 |
26 | 위 코드는 뮤테이션의 `reverseMessage()` 메서드를 이용하여 `message` 상태 값을 역순으로 변환하는 코드입니다.
27 |
28 | ## mutations 호출
29 |
30 | 컴포넌트에서 뮤테이션을 호출하려면 `commit()` API를 사용해야 합니다. 코드를 살펴보겠습니다.
31 |
32 | ```js {4}
33 | new Vue({
34 | methods: {
35 | reverseMsg() {
36 | this.$store.commit('reverseMessage');
37 | }
38 | }
39 | })
40 | ```
41 |
42 | 위 컴포넌트에서 `reverseMsg()` 메서드를 호출하면 바로 `reverseMessage()` 뮤테이션이 호출되면서 상태 값이 변환됩니다.
43 |
44 | ::: tip
45 | 뮤테이션의 개념은 methods 속성과 매칭됩니다. [메서드 예제 코드](../syntax/methods.html)와 위의 코드를 비교해보세요.
46 | :::
--------------------------------------------------------------------------------
/docs/vuex/state-vs-data.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: state vs data
3 | ---
4 |
5 | # state vs data
6 |
7 | Vue.js 애플리케이션의 규모가 커지면 컴포넌트의 data 속성과 뷰엑스(Vuex)의 [state 속성](../vuex/state.html) 중 어떤 걸 사용해야 하는지 고민하기 시작합니다. 각 속성의 정의와 어떤 상황에서 뷰엑스의 state를 쓰면 좋은지 알아보겠습니다.
8 |
9 | ## data 속성
10 |
11 | `data` 속성은 뷰의 반응성(Reactivity)이 주입된 속성입니다. 주로 데이터의 값을 화면에 표시하기 위해서 사용하는 속성입니다.
12 |
13 | ```html
14 |
15 |
{{ message }}
16 |
17 | ```
18 |
19 | ```js
20 | new Vue({
21 | data: {
22 | message: 'Hello Vue.js'
23 | }
24 | })
25 | ```
26 |
27 | 반응성이 주입되어 있기 때문에 데이터의 값이 변하면 화면이 다시 그려집니다.
28 |
29 | ```html
30 |
31 |
{{ message }}
32 |
33 | ```
34 |
35 | ```js
36 | new Vue({
37 | data: {
38 | message: 'Hello Vue.js'
39 | },
40 | methods: {
41 | changeMessage() {
42 | this.message = 'hi';
43 | }
44 | }
45 | })
46 | ```
47 |
48 | ## state 속성
49 |
50 | `state` 속성도 뷰의 반응성이 주입되어 있는 속성입니다. 아래와 같이 스토어에 정의하고 모든 컴포넌트에서 접근할 수 있습니다.
51 |
52 | ```html
53 |
{{ $store.state.message }}
54 | ```
55 |
56 | ```js
57 | new Vuex.Store({
58 | state: {
59 | message: 'Hello Vue.js'
60 | }
61 | })
62 | ```
63 |
64 | ## 접근 가능 범위
65 |
66 | `data` 속성은 해당 속성을 선언한 특정 컴포넌트에서만 접근 가능합니다. 만약 다른 컴포넌트에서 해당 `data` 속성을 접근하려면 [프롭스 속성](../vue/props.md)을 이용하여 접근해야 합니다. 반면에, `state` 속성은 스토어에 한번 정의하면 모든 컴포넌트에서 접근할 수 있습니다.
67 |
68 | ## 값 변경 방식
69 |
70 | `data` 속성은 해당 컴포넌트 내에서 자유롭게 변경할 수 있습니다. 메서드나 라이프사이클 훅 함수 등 아래와 같이 컴포넌트 내에서 `this`로 접근하여 값을 변경합니다.
71 |
72 | ```html
73 |
74 |
{{ count }}
75 |
76 |
77 | ```
78 |
79 | ```js
80 | new Vue({
81 | data: {
82 | count: 0
83 | },
84 | methods: {
85 | increment() {
86 | this.count += 1;
87 | }
88 | }
89 | })
90 | ```
91 |
92 | `state` 속성은 [뮤테이션](../vuex/mutations.html)으로만 값을 변경할 수 있습니다. 뮤테이션을 통해 변경된 값은 해당 `state` 속성을 사용하고 있는 모든 컴포넌트에 반영되어 최신 상태를 유지합니다.
93 |
94 | ```html
95 |
96 |
{{ $store.state.count }}
97 |
98 |
99 | ```
100 |
101 | ```js
102 | // count.vue
103 | new Vue({
104 | methods: {
105 | increase() {
106 | this.$store.commit('increment');
107 | }
108 | }
109 | })
110 | ```
111 |
112 | ```js
113 | // store.js
114 | new Vuex.Store({
115 | state: {
116 | count: 0
117 | },
118 | mutations: {
119 | increment(state) {
120 | state.count += 1;
121 | }
122 | }
123 | })
124 | ```
125 |
126 | ## Vuex를 언제 사용할까?
127 |
128 | 뷰엑스는 꼭 뷰엑스를 써야만 특정 기능을 구현할 수 있을 때 사용하는 것을 추천드립니다. 물론 여러 컴포넌트 간에 같은 데이터를 공유할 때는 `state` 속성을 쓰는 것이 프롭스 속성과 이벤트 에밋 방식으로 전달하는 것보다 더 편해 보입니다. 하지만, 이렇게 모든 데이터가 하나의 공간(스토어)에 밀집되는 경우 이후에 해당 공간이 더 복잡해지는 문제점이 생깁니다. 또한, 특정 UI 영역에 해당하는 데이터는 최대한 그 UI 영역에 가까이 있게 하는 것이 추후 기능 확장이나 오류 분석에도 용이합니다.
129 |
130 | 뷰엑스를 언제 써야 하는지에 대한 답은 없습니다. 스스로 컴포넌트의 범위를 구분 짓고 어떤 데이터(상태)를 어디에 선언할지 판단할 수 있을 때 뷰엑스를 더 올바르게 사용할 수 있을 거라고 생각합니다. 프롭스로 여러 번 내리고 이벤트로 여러 번 올려서 데이터 상태를 바꾸는 것이 뷰엑스보다 더 편하다면 그렇게 해도 됩니다. 이이 대한 판단은 여러분께 맡기겠습니다 :)
--------------------------------------------------------------------------------
/docs/vuex/state.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: State
3 | ---
4 |
5 | # state
6 |
7 | 상태(state)는 여러 컴포넌트 간에 공유되는 데이터를 의미합니다.
8 |
9 | ## state 선언
10 |
11 | 상태는 아래와 같이 정의합니다.
12 |
13 | ```js
14 | new Vuex.Store({
15 | state: {
16 | message: 'Hello Vue.js'
17 | }
18 | })
19 | ```
20 |
21 | 위 코드는 `message` 라는 상태 값을 정의한 코드입니다.
22 |
23 | ## state 접근
24 |
25 | 위 `message` 상태 값을 컴포넌트에서 접근하기 위해서는 아래와 같이 코딩합니다.
26 |
27 | ```html
28 |
{{ $store.state.message }}
29 | ```
30 |
31 | 코드를 실행하면 화면에 Hello Vue.js가 출력됩니다.
32 |
33 | ::: tip
34 | 아직 감이 안잡히신다면 템플릿 문법 챕터에서 Hello Vue.js를 출력하는 [코드](../vue/template.html#데이터-바인딩)와 위의 코드를 비교해보세요.
35 | :::
--------------------------------------------------------------------------------
/docs/webpack/project-setup.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Project Setup
3 | ---
4 |
5 | # Vue CLI로 생성한 프로젝트의 웹팩 설정 방법
6 |
7 | Vue CLI로 생성한 프로젝트에 웹팩 설정을 변경하려면 어떻게 해야 할까요? 웹팩 설정 확인 & 변경 방법에 대해 알아봅니다.
8 |
9 | ## Vue CLI로 생성한 프로젝트와 웹팩의 관계?
10 |
11 | 보통 Vue.js를 배우고 나면 실제 서비스를 만들 때 뷰 CLI를 이용해서 프로젝트를 생성할텐데요.
12 |
13 | ```bash
14 | vue create my-project
15 | ```
16 |
17 | 이렇게 생성된 뷰 프로젝트는 웹팩을 기반으로 동작합니다.
18 |
19 | - `npm run serve` : 로컬 서버를 웹팩 데브 서버로 실행
20 | - `npm run build` : 웹팩으로 최종 결과물 변환(빌드)
21 |
22 | ::: tip
23 | 웹팩은 최신 프런트엔드 빌드 시스템의 핵심이 되는 빌드 도구입니다. 웹팩을 잘 모르시는 분들은 [웹팩 강의](https://www.inflearn.com/course/프런트엔드-웹팩?inst=747606f7&utm_source=blog&utm_medium=githubio&utm_campaign=captianpangyo&utm_term=banner)나 [웹팩 핸드북](https://joshua1988.github.io/webpack-guide/)을 참고해서 학습해 보세요 😄
24 | :::
25 |
26 | ## 생성된 뷰 프로젝트에 웹팩 설정 파일이 안보이는데요?
27 |
28 | 뷰 CLI 버전 2 이하에서는 웹팩 설정 파일(webpack.config.js)이 프로젝트 루트 레벨에 노출되어 있었습니다. 이런 부분이 웹팩에 익숙하지 않은 사용자들에게는 부담이 되었는데요. 그래서 뷰 CLI 버전 3 이상부터는 웹팩 설정 파일을 아래와 같이 `vue-cli-service`라는 이름으로 감추어 놓았습니다.
29 |
30 | 
31 |
32 | 위 폴더 위치는 아래와 같습니다.
33 |
34 | ```
35 | src
36 | node_modules
37 | @vue
38 | cli-service
39 | ...
40 | ```
41 |
42 | 결과적으로 사용자들에게는 서버 시작 명령어와 빌드 명령어만 알려주면 웹팩은 몰라도 되게끔 기본적인 설정을 해놓은 것입니다.
43 |
44 | ```json
45 | // package.json
46 | {
47 | "scripts": {
48 | "serve": "vue-cli-service serve",
49 | "build": "vue-cli-service build"
50 | }
51 | }
52 | ```
53 |
54 | ## 그럼 프로젝트에 설정되어 있는 웹팩 옵션 속성을 어떻게 볼 수 있나요?
55 |
56 | 만약 웹팩 설정 파일 `webpack.config.js`가 프로젝트 루트 레벨로 나와 있다면 바로 보면 되겠지만 `vue-cli-service` 내부에 여러 파일들로 나뉘어져 있어 한눈에 파악하기 어렵습니다. 이 때 프로젝트에 설정된 웹팩 옵션들을 보고 싶다면 아래 명령어를 입력합니다.
57 |
58 | ```sh
59 | vue inspect > options.js
60 | ```
61 |
62 | 위 명령어를 치면 프로젝트 내부적으로 설정된 웹팩의 옵션들을 `options.js` 파일에 담아줍니다. 이제 `options.js` 파일을 열어보면 아래 내용을 확인할 수 있습니다.
63 |
64 | 
65 |
66 | ## 프로젝트의 웹팩 설정을 수정하고 싶다면?
67 |
68 | 프로젝트에 설정되어 있는 웹팩의 옵션들을 변경하고 싶다면 `vue.config.js` 파일을 변경해야 합니다.
69 |
70 | ```js
71 | // vue.config.js
72 | module.exports = {
73 | outputDir: 'my-dist',
74 | }
75 | ```
76 |
77 | 위 `outputDir` 속성은 웹팩 설정 파일의 [output.path](https://webpack.js.org/configuration/output/#outputpath)와 동일한 옵션입니다. 이렇게 [뷰 CLI의 공식 가이드에 안내된 내용](https://cli.vuejs.org/config/#vue-config-js)을 따라 웹팩 설정을 변경해 주셔야 최종 빌드에서 사용자가 변경한 값이 함께 적용됩니다.
78 |
79 | 이외에도 아래와 같은 옵션들을 이용하여 웹팩 설정을 추가 또는 체이닝을 할 수 있습니다.
80 |
81 | ```js
82 | // vue.config.js
83 | module.exports = {
84 | // 옵션 추가
85 | configureWebpack: {
86 | plugins: [
87 | new MyAwesomeWebpackPlugin()
88 | ]
89 | },
90 | // 옵션 체이닝
91 | configureWebpack: config => {
92 | if (process.env.NODE_ENV === 'production') {
93 | // mutate config for production...
94 | } else {
95 | // mutate for development...
96 | }
97 | }
98 | }
99 | ```
100 |
101 | [옵션 추가와 체이닝 방법의 정확한 가이드](https://cli.vuejs.org/guide/webpack.html)는 CLI 공식 문서를 참고하세요 :)
--------------------------------------------------------------------------------
/efe.sh:
--------------------------------------------------------------------------------
1 | npm run dev
--------------------------------------------------------------------------------
/img/filter-googling.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/img/filter-googling.png
--------------------------------------------------------------------------------
/img/issue-sample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/img/issue-sample.png
--------------------------------------------------------------------------------
/img/prototype_001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/img/prototype_001.png
--------------------------------------------------------------------------------
/img/prototype_002.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/img/prototype_002.png
--------------------------------------------------------------------------------
/img/prototype_003.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/img/prototype_003.png
--------------------------------------------------------------------------------
/img/schedule.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/img/schedule.png
--------------------------------------------------------------------------------
/img/vite-network.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshua1988/vue-camp/1d2b5ed08f37e891e5dd2c762a9cc25d2351a9dc/img/vite-network.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-camp",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "directories": {
7 | "doc": "docs"
8 | },
9 | "scripts": {
10 | "dev": "vuepress dev docs",
11 | "build": "vuepress build docs",
12 | "insert": "all-contributors add",
13 | "generate": "all-contributors generate",
14 | "init-husky": "npx husky install .husky",
15 | "lint-front": "lint-staged",
16 | "prettier": "prettier **/*/*.md --write"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/joshua1988/vue-camp.git"
21 | },
22 | "lint-staged": {
23 | "docs/**/*.js": [
24 | "prettier --write"
25 | ],
26 | "**/*/*.md": [
27 | "prettier --write",
28 | "git add"
29 | ]
30 | },
31 | "keywords": [],
32 | "author": "",
33 | "license": "ISC",
34 | "bugs": {
35 | "url": "https://github.com/joshua1988/vue-camp/issues"
36 | },
37 | "homepage": "https://github.com/joshua1988/vue-camp#readme",
38 | "devDependencies": {
39 | "@vuepress/plugin-back-to-top": "^1.5.2",
40 | "@vuepress/plugin-google-analytics": "^1.1.0",
41 | "@vuepress/plugin-last-updated": "^1.8.2",
42 | "@vuepress/plugin-pwa": "^1.8.2",
43 | "all-contributors-cli": "^6.20.0",
44 | "husky": "^4.3.8",
45 | "lint-staged": "^11.1.2",
46 | "prettier": "^2.4.0",
47 | "vuepress": "^1.8.2",
48 | "vuepress-plugin-code-copy": "^1.0.6",
49 | "vuepress-plugin-reading-progress": "^1.0.10"
50 | },
51 | "resolutions": {
52 | "webpack-dev-middleware": "3.6.0"
53 | },
54 | "engines": {
55 | "node": "14.19.1"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | { "source": "/vue-camp/(.*)", "destination": "/$1" }
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------