From e95b0b451fd645a3cde8001133c885f7708727aa Mon Sep 17 00:00:00 2001 From: LEE-YO-HAN Date: Wed, 27 Dec 2023 07:09:40 +0900 Subject: [PATCH] Updates --- 404.html | 2 +- _next/data/OV20HWrLkXuRM8jaFvyc0/cs-sw-test.json | 1 - _next/data/OV20HWrLkXuRM8jaFvyc0/cs.json | 1 - _next/data/OV20HWrLkXuRM8jaFvyc0/index.json | 1 - .../css-a-tag-style-fix.json | 0 .../css-no-selection.json | 0 .../css-use-download-font.json | 0 .../{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/css.json | 0 .../github-git-blog-utterances-comment.json | 0 .../github-git-cmd.json | 0 .../github-git-custom-blog.json | 0 .../github-git-use-markdown-viewer.json | 0 .../github-github-api-get-repo-data.json | 0 .../github-markdown-syntex.json | 0 .../github-token-expired.json | 0 .../github.json | 0 .../html-input-basic.json | 0 .../html-meta-tag.json | 0 .../html.json | 0 _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/index.json | 1 + .../javascript-basic-1-num-str.json | 0 .../javascript-basic-2-date.json | 0 .../javascript-basic-3-math.json | 0 .../javascript-basic-4-array-1.json | 0 .../javascript-basic-4-array-2.json | 0 .../javascript-basic-5-loop.json | 0 .../javascript-dynamic-import.json | 0 .../javascript-how-to-use-promise.all.json | 0 .../javascript-jest-test-code.json | 0 .../javascript.json | 0 .../nestjs-custom-repository.json | 0 .../nestjs-localhost-https.json | 0 .../nestjs-server-client-cookie.json | 0 .../nestjs-server-client-cookie2.json | 0 .../nestjs-use-bcrypt.json | 0 .../nestjs.json | 0 .../nextjs-link-userouter.json | 0 .../nextjs-loading.json | 0 .../nextjs-marked-webpack-imported-module-7-default.json | 0 .../nextjs-pages-api-dir-unsupported.json | 0 .../nextjs-prop-classname-did-not-match.json | 0 .../nextjs-react-responsive.json | 0 .../nextjs.json | 0 .../react-common-components.json | 0 .../react-component-lifecycle.json | 0 .../react-context-api.json | 0 .../react-datepicker.json | 0 .../react-media-query.json | 0 .../react-memo.json | 0 .../react-modal-non-library.json | 0 .../react-navigate-props.json | 0 .../react-simple-formdata-code.json | 0 .../react-smooth-scroll.json | 0 .../react-styled-components-hover.json | 0 .../react-submit-prevent-default.json | 0 .../react-type-assertion.json | 0 .../react-usecallback.json | 0 .../react-usememo.json | 0 .../react-z-index-error.json | 0 .../react.json | 0 .../redux-mock-server.json | 0 .../redux-toolkit-async-thunk.json | 0 .../redux-typescript-react-reduxtoolkit.json | 0 .../redux-usage.json | 0 .../redux.json | 0 _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/sw-test.json | 1 + _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/sw.json | 1 + .../typescript-syntax.json | 0 .../typescript.json | 0 _next/static/OV20HWrLkXuRM8jaFvyc0/_buildManifest.js | 1 - _next/static/OV20HWrLkXuRM8jaFvyc0/_ssgManifest.js | 1 - _next/static/TS_Y-VMLzDgBVGe3Zk5Yz/_buildManifest.js | 1 + _next/static/TS_Y-VMLzDgBVGe3Zk5Yz/_ssgManifest.js | 1 + .../{_app-9ab2984ff460f65e.js => _app-80b9c37206a02b35.js} | 2 +- _next/static/chunks/pages/cs-3fbdfdd36b3aab17.js | 1 - _next/static/chunks/pages/sw-9b1fdcf3f8a80910.js | 1 + cs.html | 1 - css-a-tag-style-fix.html | 4 ++-- css-no-selection.html | 4 ++-- css-use-download-font.html | 4 ++-- css.html | 2 +- github-git-blog-utterances-comment.html | 4 ++-- github-git-cmd.html | 4 ++-- github-git-custom-blog.html | 4 ++-- github-git-use-markdown-viewer.html | 4 ++-- github-github-api-get-repo-data.html | 4 ++-- github-markdown-syntex.html | 4 ++-- github-token-expired.html | 4 ++-- github.html | 2 +- html-input-basic.html | 4 ++-- html-meta-tag.html | 4 ++-- html.html | 2 +- index.html | 2 +- javascript-basic-1-num-str.html | 4 ++-- javascript-basic-2-date.html | 4 ++-- javascript-basic-3-math.html | 4 ++-- javascript-basic-4-array-1.html | 4 ++-- javascript-basic-4-array-2.html | 4 ++-- javascript-basic-5-loop.html | 4 ++-- javascript-dynamic-import.html | 4 ++-- javascript-how-to-use-promise.all.html | 4 ++-- javascript-jest-test-code.html | 4 ++-- javascript.html | 2 +- nestjs-custom-repository.html | 4 ++-- nestjs-localhost-https.html | 4 ++-- nestjs-server-client-cookie.html | 4 ++-- nestjs-server-client-cookie2.html | 4 ++-- nestjs-use-bcrypt.html | 4 ++-- nestjs.html | 2 +- nextjs-link-userouter.html | 4 ++-- nextjs-loading.html | 4 ++-- nextjs-marked-webpack-imported-module-7-default.html | 4 ++-- nextjs-pages-api-dir-unsupported.html | 4 ++-- nextjs-prop-classname-did-not-match.html | 4 ++-- nextjs-react-responsive.html | 4 ++-- nextjs.html | 2 +- profile.html | 2 +- react-common-components.html | 4 ++-- react-component-lifecycle.html | 4 ++-- react-context-api.html | 4 ++-- react-datepicker.html | 4 ++-- react-media-query.html | 4 ++-- react-memo.html | 4 ++-- react-modal-non-library.html | 4 ++-- react-navigate-props.html | 4 ++-- react-simple-formdata-code.html | 4 ++-- react-smooth-scroll.html | 4 ++-- react-styled-components-hover.html | 4 ++-- react-submit-prevent-default.html | 4 ++-- react-type-assertion.html | 4 ++-- react-usecallback.html | 4 ++-- react-usememo.html | 4 ++-- react-z-index-error.html | 4 ++-- react.html | 2 +- redux-mock-server.html | 4 ++-- redux-toolkit-async-thunk.html | 4 ++-- redux-typescript-react-reduxtoolkit.html | 4 ++-- redux-usage.html | 4 ++-- redux.html | 2 +- search.html | 2 +- cs-sw-test.html => sw-test.html | 4 ++-- sw.html | 1 + typescript-syntax.html | 4 ++-- typescript.html | 2 +- 144 files changed, 129 insertions(+), 129 deletions(-) delete mode 100644 _next/data/OV20HWrLkXuRM8jaFvyc0/cs-sw-test.json delete mode 100644 _next/data/OV20HWrLkXuRM8jaFvyc0/cs.json delete mode 100644 _next/data/OV20HWrLkXuRM8jaFvyc0/index.json rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/css-a-tag-style-fix.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/css-no-selection.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/css-use-download-font.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/css.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/github-git-blog-utterances-comment.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/github-git-cmd.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/github-git-custom-blog.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/github-git-use-markdown-viewer.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/github-github-api-get-repo-data.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/github-markdown-syntex.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/github-token-expired.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/github.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/html-input-basic.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/html-meta-tag.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/html.json (100%) create mode 100644 _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/index.json rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/javascript-basic-1-num-str.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/javascript-basic-2-date.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/javascript-basic-3-math.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/javascript-basic-4-array-1.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/javascript-basic-4-array-2.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/javascript-basic-5-loop.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/javascript-dynamic-import.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/javascript-how-to-use-promise.all.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/javascript-jest-test-code.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/javascript.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nestjs-custom-repository.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nestjs-localhost-https.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nestjs-server-client-cookie.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nestjs-server-client-cookie2.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nestjs-use-bcrypt.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nestjs.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nextjs-link-userouter.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nextjs-loading.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nextjs-marked-webpack-imported-module-7-default.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nextjs-pages-api-dir-unsupported.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nextjs-prop-classname-did-not-match.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nextjs-react-responsive.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/nextjs.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-common-components.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-component-lifecycle.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-context-api.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-datepicker.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-media-query.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-memo.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-modal-non-library.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-navigate-props.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-simple-formdata-code.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-smooth-scroll.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-styled-components-hover.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-submit-prevent-default.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-type-assertion.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-usecallback.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-usememo.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react-z-index-error.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/react.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/redux-mock-server.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/redux-toolkit-async-thunk.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/redux-typescript-react-reduxtoolkit.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/redux-usage.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/redux.json (100%) create mode 100644 _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/sw-test.json create mode 100644 _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/sw.json rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/typescript-syntax.json (100%) rename _next/data/{OV20HWrLkXuRM8jaFvyc0 => TS_Y-VMLzDgBVGe3Zk5Yz}/typescript.json (100%) delete mode 100644 _next/static/OV20HWrLkXuRM8jaFvyc0/_buildManifest.js delete mode 100644 _next/static/OV20HWrLkXuRM8jaFvyc0/_ssgManifest.js create mode 100644 _next/static/TS_Y-VMLzDgBVGe3Zk5Yz/_buildManifest.js create mode 100644 _next/static/TS_Y-VMLzDgBVGe3Zk5Yz/_ssgManifest.js rename _next/static/chunks/pages/{_app-9ab2984ff460f65e.js => _app-80b9c37206a02b35.js} (99%) delete mode 100644 _next/static/chunks/pages/cs-3fbdfdd36b3aab17.js create mode 100644 _next/static/chunks/pages/sw-9b1fdcf3f8a80910.js delete mode 100644 cs.html rename cs-sw-test.html => sw-test.html (82%) create mode 100644 sw.html diff --git a/404.html b/404.html index 1e27b59d..70eb7dd4 100644 --- a/404.html +++ b/404.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/cs-sw-test.json b/_next/data/OV20HWrLkXuRM8jaFvyc0/cs-sw-test.json deleted file mode 100644 index 444de502..00000000 --- a/_next/data/OV20HWrLkXuRM8jaFvyc0/cs-sw-test.json +++ /dev/null @@ -1 +0,0 @@ -{"pageProps":{"title":"cs sw test","description":"정보처리기사 테스트","category":"cs","date":"2023-12-27","content":"\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n```typescript\r\n\r\n// 문자열\r\nlet str: string = \"hello\";\r\n\r\n// 숫자\r\nlet num: number = 100;\r\n\r\n// 배열\r\nlet arr: Array = [10,20,30];\r\nlet arr2: number[] = [10,20,30];\r\nlet arr3: Array = [\"hello\",\"hellololo\" ];\r\nlet arr4: [string, number] = [\"hello\", 182];\r\n\r\n// 객체\r\nlet obj:object = {name:\"hello\", age:29};\r\nlet person:{name: string, age:number};\r\n\r\n// Boolean\r\nlet isAvaliable: boolean = true;\r\n\r\n\r\n// 함수 선언\r\nparameter와 return 값에 대해 타입 선언 가능\r\nconst sum = (a:number, b:number):number => {\r\nreturn a+b;\r\n}\r\n\r\n// optional parameter일 경우 ?를 사용\r\n\r\nconst log = (time: string, result?: string, option?: string) => {\r\nconsole.log(time, result, option);\r\n}\r\nlog(\"2021-10-04\", \"success\");\r\n\r\n```\r\n\r\n# 인터페이스 (interface)\r\n\r\n### 자주 사용하는 타입들을 object 형태의 묶음으로 정의해 새로운 타입을 만들어 사용하는 기능\r\n\r\n```typescript\r\n\r\n// interface 선언\r\ninterface User {\r\nage: number;\r\nname: string;\r\n}\r\n\r\n// 변수 활용\r\nconst hanbbi: User = { age: 30, name: \"hello\"}\r\n\r\n\r\n// 함수 인자로의 활용\r\nconst getUser = (user:User){\r\nconsole.log(user);\r\n}\r\ngetUser({ age:10, name: \"hanbbi\" })\r\n\r\n\r\n\r\n// 함수 구조 활용\r\ninterface Sum {\r\n(a:number, b:number): number;\r\n}\r\n\r\nlet sumFinc: Sum:\r\nsumFunc = function(a: number, b: number): number {\r\nreturn a+b;\r\n}\r\n\r\n\r\n\r\n// 배열 활용\r\ninterface StringArray {\r\n[index:number]: string;\r\n}\r\n\r\nlet arr: StringArray = [\"a\", \"b\", \"c\"];\r\n\r\n\r\n// 객체 활용\r\ninterface StringRegexObject {\r\n[key: string]: RegExp;\r\n}\r\n\r\nconst obj: StringRegexObject {\r\ncssFile: /\\.css$/,\r\n jsFile: /\\.js$/\r\n}\r\n\r\n\r\n// interface 확장 (extends 사용)\r\ninterface Person {\r\nname: string;\r\nage:number;\r\n}\r\ninterface Developer extends Person {\r\nskill: string;\r\n}\r\n\r\nconst juniorDeveloper = {\r\nname: \"hanbbi\",\r\nage:100,\r\nskill: \"JS\"\r\n}\r\n\r\n```\r\n\r\n# 타입 별칭(type aliases)\r\n\r\n### 타입 키워드는 interface와 다르게 새로운 타입을 생성하는 것이 아닌 별칭을 부여하는 것으로, extends 키워드는 사용할 수 없음\r\n\r\n```typescript\r\n\r\n// 타입 별칭 선언 및 활용\r\ntype MyString = string;\r\nconst str: MyString = \"Hello dear\"\r\n\r\ntype Todo = {\r\nid: string;\r\ntitle: string;\r\ndone: boolean\r\n}\r\n\r\nconst getTodo(todo:Todo){\r\nconsole.log(todo);\r\n}\r\n\r\n```\r\n\r\n# 연산자 (Operator)\r\n\r\n## Uinon Type\r\n\r\n### 한 가지 이상의 type을 선언하고자 할 때 사용 가능. | 기호 사용\r\n\r\n```typescript\r\nconst logMessage = (value: string | number) => {\r\n if (typeof value === \"string\") {\r\n value.toString();\r\n } else if (typeof value === \"number\") {\r\n value.toLocaleString();\r\n } else {\r\n throw new TypeError(\"value must be string or number\");\r\n }\r\n};\r\nlogMessage(\"hello\");\r\nlogMessage(1000);\r\n```\r\n\r\n## intersection Type\r\n\r\n### 합집합과 같은 개념으로, 함수 호출의 경우 함수 인자에 명시한 type을 모두 제공해야 한다. & 기호 사용\r\n\r\n```typescript\r\n\r\ninterface Zoo {\r\nname: string;\r\nlocation: string\r\nprice: number;\r\n}\r\n\r\ninterface Animal {\r\nname: string;\r\ncount: number;\r\n}\r\n\r\nconst askZookeeper = ( value : Zoo & Animal ) => {\r\n // value 는\r\n { name:\"어린이대공원\", location: \"서울시 광진구\", price: 10000, count: 10000}\r\n // 와 같이 Zoo와 Animal이 모두 포함되는 인자를 줘야한다.\r\n}\r\n\r\n```\r\n\r\n# Enum\r\n\r\n### enum 키워드를 사용하면 일종의 default 값을 선언할 수 있다.\r\n\r\n```typescript\r\n\r\n// 숫자형 enum\r\n// 자동으로 0에서 1씩 증가하는 값을 부여\r\n\r\nenum Shoes {\r\nNike, // 0\r\nAdidas, // 1\r\nNewBalance //2\r\n}\r\nconst myShoes = Shoes.Nike; // 0\r\n\r\n문자형 enum\r\nenum Food {\r\ncake = \"케익\",\r\ncookie = \"쿠키\"\r\n}\r\nconst player = Food.cookie; // 쿠키\r\n\r\n```\r\n\r\n# 제네릭\r\n\r\n### 제네릭을 활용하면 인자를 넘겨 호출하는 시점에 타입을 결정할 수 있다. 제네릭 활용 시 동일한 기능을 하는 함수를 일일이 만들 필요가 없으며, 타입 추론에 있어 강점을 가진다.\r\n\r\n제네릭 선언\r\n와 같이 타입을 선언한다. 알파벳은 통상 T로 정해져 있다.\r\n\r\n```typescript\r\n\r\nconst logText = (text: T):T => {\r\nconsole.log(text);\r\nreturn text;\r\n}\r\nlogText(\"Hello hanbbi\");\r\n\r\n\r\n// interface에 제네릭 선언\r\n\r\ninterface Dropdown {\r\nvalue: T;\r\nselected: boolean;\r\n}\r\ncosnt obj: Dropdown = { value: \"hamburger\" , selected: true};\r\n\r\n```\r\n\r\n# 제네릭 타입 제한\r\n\r\n## 1. 배열 힌트\r\n\r\n```typescript\r\n\r\nconst logTextLength = (text: T[]): T[] =>{\r\nconsole.log(text.length);\r\ntext.forEach(text =>{\r\nconsole.log(text):\r\n});\r\n}\r\nlogTextLength([\"hi\", \"hello\"]);\r\n\r\n\r\n```\r\n\r\n## 2. 정의된 타입 이용(extends)과 keyof\r\n\r\n```typescript\r\n\r\ninterface ShoppingItem {\r\nname: string;\r\nprice: number;\r\nstock: number;\r\n}\r\n\r\nconst getShoppingItemOption(itemOption: T): T {\r\nreturn itemOption;\r\n}\r\n\r\n// \"name\", \"price\", \"stock\"만 인자로 가능\r\ngetShoppingItemOption(\"price\");\r\n\r\n```\r\n\r\n# 타입 추론 (Type inference)\r\n\r\n## 1. 기본 변수 타입 추론\r\n\r\n```typescript\r\n\r\n// string으로 추론\r\nlet a = \"abc\";\r\n\r\n// a: number로 추론\r\n// b: string으로 추론\r\n// return value는 string으로 추론\r\nconst getValue(a = 10) {\r\nlet b = \"hello\";\r\nreturn a + b;\r\n}\r\n\r\n\r\n```\r\n\r\n## 2. interface추론\r\n\r\n```typescript\r\n\r\n// interface 1개\r\ninterface Dropdown {\r\nvalue: T;\r\ntitle: string;\r\n}\r\nconst shoppingItem:Dropdown ={\r\nvalue: 10000;\r\ntitle: \"shoe\"\r\n}\r\n// interface 2개\r\ninterface Dropdown2 {\r\nvalue: T;\r\ntitle: string;\r\n}\r\ninterface DetailedDropdown extends Dropdown2{\r\ntag: K;\r\ndesc: string;\r\n}\r\nconst detailed: DetailedDropdown{\r\nvalue: \"10000\";\r\ntitle: \"shoe\",\r\ntag: \"10000\",\r\ndesc: \"description\"\r\n}\r\n\r\n```\r\n\r\n# 타입 단언 (Type assertion)\r\n\r\n### as 키워드를 사용해 타입을 정함으로써 typescript에게 타입을 알려줄 수 있다. 주로 DOM API를 조작할 떄 사용한다.\r\n\r\n```typescript\r\n// div가 있는지 장담할 수 없음, HTMLDivElement | null\r\n// 따라서 typescript에게 타입을 단언해 타입을 알려줄 수 있음.\r\nconst div = document.querySelector(\"div\") as HTMLDivElement;\r\ndiv.innerText = \"test\";\r\n```\r\n\r\n# 타입 가드 (Type guard)\r\n\r\n### union type을 사용하는 경우 공통된 속성만 접근 가능하며, 로직상 공통되지 않은 속성에 접근하고자 할 때 불편함을 해소하기 위해 타입 단언으로 공통되지 않은 속성에 접근하고자 하는 방법이 있지만, 코드 가독성을 위해 타입 가드 방법을 주로 사용한다.\r\n\r\n```typescript\r\nconst isDeveloper = (target: Developer | Humanoid): target is Developer => {\r\n return (target as Developer).skill !== undefined;\r\n};\r\nif (isDeveloper(tom)) {\r\n console.log(tom.name);\r\n console.log(tom.skill);\r\n} else {\r\n console.log(tom.name);\r\n console.log(tom.age);\r\n}\r\n```\r\n\r\n# 타입 호환 (Type compatibility)\r\n\r\n### TypeScript에서 더 큰 타입 구조를 갖는 변수에 작은 타입 구조를 갖는 변수를 할당 가능\r\n\r\n```typescript\r\nlet add = function (a: number) {\r\n // ...\r\n};\r\nlet sum = function (a: number, b: number) {\r\n // ...\r\n};\r\n// 아래는 에러가\r\n// add = sum;\r\n\r\n// 에러가 나지 않는 방식. sum의 구조가 더 크다고 볼 수 있음\r\nsum = add;\r\n```\r\n\r\n참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/\r\n"},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/cs.json b/_next/data/OV20HWrLkXuRM8jaFvyc0/cs.json deleted file mode 100644 index e152433f..00000000 --- a/_next/data/OV20HWrLkXuRM8jaFvyc0/cs.json +++ /dev/null @@ -1 +0,0 @@ -{"pageProps":{"posts":[{"title":"cs sw test","description":"정보처리기사 테스트","category":"cs","date":"2023-12-27","content":"\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n```typescript\r\n\r\n// 문자열\r\nlet str: string = \"hello\";\r\n\r\n// 숫자\r\nlet num: number = 100;\r\n\r\n// 배열\r\nlet arr: Array = [10,20,30];\r\nlet arr2: number[] = [10,20,30];\r\nlet arr3: Array = [\"hello\",\"hellololo\" ];\r\nlet arr4: [string, number] = [\"hello\", 182];\r\n\r\n// 객체\r\nlet obj:object = {name:\"hello\", age:29};\r\nlet person:{name: string, age:number};\r\n\r\n// Boolean\r\nlet isAvaliable: boolean = true;\r\n\r\n\r\n// 함수 선언\r\nparameter와 return 값에 대해 타입 선언 가능\r\nconst sum = (a:number, b:number):number => {\r\nreturn a+b;\r\n}\r\n\r\n// optional parameter일 경우 ?를 사용\r\n\r\nconst log = (time: string, result?: string, option?: string) => {\r\nconsole.log(time, result, option);\r\n}\r\nlog(\"2021-10-04\", \"success\");\r\n\r\n```\r\n\r\n# 인터페이스 (interface)\r\n\r\n### 자주 사용하는 타입들을 object 형태의 묶음으로 정의해 새로운 타입을 만들어 사용하는 기능\r\n\r\n```typescript\r\n\r\n// interface 선언\r\ninterface User {\r\nage: number;\r\nname: string;\r\n}\r\n\r\n// 변수 활용\r\nconst hanbbi: User = { age: 30, name: \"hello\"}\r\n\r\n\r\n// 함수 인자로의 활용\r\nconst getUser = (user:User){\r\nconsole.log(user);\r\n}\r\ngetUser({ age:10, name: \"hanbbi\" })\r\n\r\n\r\n\r\n// 함수 구조 활용\r\ninterface Sum {\r\n(a:number, b:number): number;\r\n}\r\n\r\nlet sumFinc: Sum:\r\nsumFunc = function(a: number, b: number): number {\r\nreturn a+b;\r\n}\r\n\r\n\r\n\r\n// 배열 활용\r\ninterface StringArray {\r\n[index:number]: string;\r\n}\r\n\r\nlet arr: StringArray = [\"a\", \"b\", \"c\"];\r\n\r\n\r\n// 객체 활용\r\ninterface StringRegexObject {\r\n[key: string]: RegExp;\r\n}\r\n\r\nconst obj: StringRegexObject {\r\ncssFile: /\\.css$/,\r\n jsFile: /\\.js$/\r\n}\r\n\r\n\r\n// interface 확장 (extends 사용)\r\ninterface Person {\r\nname: string;\r\nage:number;\r\n}\r\ninterface Developer extends Person {\r\nskill: string;\r\n}\r\n\r\nconst juniorDeveloper = {\r\nname: \"hanbbi\",\r\nage:100,\r\nskill: \"JS\"\r\n}\r\n\r\n```\r\n\r\n# 타입 별칭(type aliases)\r\n\r\n### 타입 키워드는 interface와 다르게 새로운 타입을 생성하는 것이 아닌 별칭을 부여하는 것으로, extends 키워드는 사용할 수 없음\r\n\r\n```typescript\r\n\r\n// 타입 별칭 선언 및 활용\r\ntype MyString = string;\r\nconst str: MyString = \"Hello dear\"\r\n\r\ntype Todo = {\r\nid: string;\r\ntitle: string;\r\ndone: boolean\r\n}\r\n\r\nconst getTodo(todo:Todo){\r\nconsole.log(todo);\r\n}\r\n\r\n```\r\n\r\n# 연산자 (Operator)\r\n\r\n## Uinon Type\r\n\r\n### 한 가지 이상의 type을 선언하고자 할 때 사용 가능. | 기호 사용\r\n\r\n```typescript\r\nconst logMessage = (value: string | number) => {\r\n if (typeof value === \"string\") {\r\n value.toString();\r\n } else if (typeof value === \"number\") {\r\n value.toLocaleString();\r\n } else {\r\n throw new TypeError(\"value must be string or number\");\r\n }\r\n};\r\nlogMessage(\"hello\");\r\nlogMessage(1000);\r\n```\r\n\r\n## intersection Type\r\n\r\n### 합집합과 같은 개념으로, 함수 호출의 경우 함수 인자에 명시한 type을 모두 제공해야 한다. & 기호 사용\r\n\r\n```typescript\r\n\r\ninterface Zoo {\r\nname: string;\r\nlocation: string\r\nprice: number;\r\n}\r\n\r\ninterface Animal {\r\nname: string;\r\ncount: number;\r\n}\r\n\r\nconst askZookeeper = ( value : Zoo & Animal ) => {\r\n // value 는\r\n { name:\"어린이대공원\", location: \"서울시 광진구\", price: 10000, count: 10000}\r\n // 와 같이 Zoo와 Animal이 모두 포함되는 인자를 줘야한다.\r\n}\r\n\r\n```\r\n\r\n# Enum\r\n\r\n### enum 키워드를 사용하면 일종의 default 값을 선언할 수 있다.\r\n\r\n```typescript\r\n\r\n// 숫자형 enum\r\n// 자동으로 0에서 1씩 증가하는 값을 부여\r\n\r\nenum Shoes {\r\nNike, // 0\r\nAdidas, // 1\r\nNewBalance //2\r\n}\r\nconst myShoes = Shoes.Nike; // 0\r\n\r\n문자형 enum\r\nenum Food {\r\ncake = \"케익\",\r\ncookie = \"쿠키\"\r\n}\r\nconst player = Food.cookie; // 쿠키\r\n\r\n```\r\n\r\n# 제네릭\r\n\r\n### 제네릭을 활용하면 인자를 넘겨 호출하는 시점에 타입을 결정할 수 있다. 제네릭 활용 시 동일한 기능을 하는 함수를 일일이 만들 필요가 없으며, 타입 추론에 있어 강점을 가진다.\r\n\r\n제네릭 선언\r\n와 같이 타입을 선언한다. 알파벳은 통상 T로 정해져 있다.\r\n\r\n```typescript\r\n\r\nconst logText = (text: T):T => {\r\nconsole.log(text);\r\nreturn text;\r\n}\r\nlogText(\"Hello hanbbi\");\r\n\r\n\r\n// interface에 제네릭 선언\r\n\r\ninterface Dropdown {\r\nvalue: T;\r\nselected: boolean;\r\n}\r\ncosnt obj: Dropdown = { value: \"hamburger\" , selected: true};\r\n\r\n```\r\n\r\n# 제네릭 타입 제한\r\n\r\n## 1. 배열 힌트\r\n\r\n```typescript\r\n\r\nconst logTextLength = (text: T[]): T[] =>{\r\nconsole.log(text.length);\r\ntext.forEach(text =>{\r\nconsole.log(text):\r\n});\r\n}\r\nlogTextLength([\"hi\", \"hello\"]);\r\n\r\n\r\n```\r\n\r\n## 2. 정의된 타입 이용(extends)과 keyof\r\n\r\n```typescript\r\n\r\ninterface ShoppingItem {\r\nname: string;\r\nprice: number;\r\nstock: number;\r\n}\r\n\r\nconst getShoppingItemOption(itemOption: T): T {\r\nreturn itemOption;\r\n}\r\n\r\n// \"name\", \"price\", \"stock\"만 인자로 가능\r\ngetShoppingItemOption(\"price\");\r\n\r\n```\r\n\r\n# 타입 추론 (Type inference)\r\n\r\n## 1. 기본 변수 타입 추론\r\n\r\n```typescript\r\n\r\n// string으로 추론\r\nlet a = \"abc\";\r\n\r\n// a: number로 추론\r\n// b: string으로 추론\r\n// return value는 string으로 추론\r\nconst getValue(a = 10) {\r\nlet b = \"hello\";\r\nreturn a + b;\r\n}\r\n\r\n\r\n```\r\n\r\n## 2. interface추론\r\n\r\n```typescript\r\n\r\n// interface 1개\r\ninterface Dropdown {\r\nvalue: T;\r\ntitle: string;\r\n}\r\nconst shoppingItem:Dropdown ={\r\nvalue: 10000;\r\ntitle: \"shoe\"\r\n}\r\n// interface 2개\r\ninterface Dropdown2 {\r\nvalue: T;\r\ntitle: string;\r\n}\r\ninterface DetailedDropdown extends Dropdown2{\r\ntag: K;\r\ndesc: string;\r\n}\r\nconst detailed: DetailedDropdown{\r\nvalue: \"10000\";\r\ntitle: \"shoe\",\r\ntag: \"10000\",\r\ndesc: \"description\"\r\n}\r\n\r\n```\r\n\r\n# 타입 단언 (Type assertion)\r\n\r\n### as 키워드를 사용해 타입을 정함으로써 typescript에게 타입을 알려줄 수 있다. 주로 DOM API를 조작할 떄 사용한다.\r\n\r\n```typescript\r\n// div가 있는지 장담할 수 없음, HTMLDivElement | null\r\n// 따라서 typescript에게 타입을 단언해 타입을 알려줄 수 있음.\r\nconst div = document.querySelector(\"div\") as HTMLDivElement;\r\ndiv.innerText = \"test\";\r\n```\r\n\r\n# 타입 가드 (Type guard)\r\n\r\n### union type을 사용하는 경우 공통된 속성만 접근 가능하며, 로직상 공통되지 않은 속성에 접근하고자 할 때 불편함을 해소하기 위해 타입 단언으로 공통되지 않은 속성에 접근하고자 하는 방법이 있지만, 코드 가독성을 위해 타입 가드 방법을 주로 사용한다.\r\n\r\n```typescript\r\nconst isDeveloper = (target: Developer | Humanoid): target is Developer => {\r\n return (target as Developer).skill !== undefined;\r\n};\r\nif (isDeveloper(tom)) {\r\n console.log(tom.name);\r\n console.log(tom.skill);\r\n} else {\r\n console.log(tom.name);\r\n console.log(tom.age);\r\n}\r\n```\r\n\r\n# 타입 호환 (Type compatibility)\r\n\r\n### TypeScript에서 더 큰 타입 구조를 갖는 변수에 작은 타입 구조를 갖는 변수를 할당 가능\r\n\r\n```typescript\r\nlet add = function (a: number) {\r\n // ...\r\n};\r\nlet sum = function (a: number, b: number) {\r\n // ...\r\n};\r\n// 아래는 에러가\r\n// add = sum;\r\n\r\n// 에러가 나지 않는 방식. sum의 구조가 더 크다고 볼 수 있음\r\nsum = add;\r\n```\r\n\r\n참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/\r\n"}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/index.json b/_next/data/OV20HWrLkXuRM8jaFvyc0/index.json deleted file mode 100644 index 8342354b..00000000 --- a/_next/data/OV20HWrLkXuRM8jaFvyc0/index.json +++ /dev/null @@ -1 +0,0 @@ -{"pageProps":{"posts":[{"title":"cs sw test","description":"정보처리기사 테스트","category":"cs","date":"2023-12-27","content":"\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n```typescript\r\n\r\n// 문자열\r\nlet str: string = \"hello\";\r\n\r\n// 숫자\r\nlet num: number = 100;\r\n\r\n// 배열\r\nlet arr: Array = [10,20,30];\r\nlet arr2: number[] = [10,20,30];\r\nlet arr3: Array = [\"hello\",\"hellololo\" ];\r\nlet arr4: [string, number] = [\"hello\", 182];\r\n\r\n// 객체\r\nlet obj:object = {name:\"hello\", age:29};\r\nlet person:{name: string, age:number};\r\n\r\n// Boolean\r\nlet isAvaliable: boolean = true;\r\n\r\n\r\n// 함수 선언\r\nparameter와 return 값에 대해 타입 선언 가능\r\nconst sum = (a:number, b:number):number => {\r\nreturn a+b;\r\n}\r\n\r\n// optional parameter일 경우 ?를 사용\r\n\r\nconst log = (time: string, result?: string, option?: string) => {\r\nconsole.log(time, result, option);\r\n}\r\nlog(\"2021-10-04\", \"success\");\r\n\r\n```\r\n\r\n# 인터페이스 (interface)\r\n\r\n### 자주 사용하는 타입들을 object 형태의 묶음으로 정의해 새로운 타입을 만들어 사용하는 기능\r\n\r\n```typescript\r\n\r\n// interface 선언\r\ninterface User {\r\nage: number;\r\nname: string;\r\n}\r\n\r\n// 변수 활용\r\nconst hanbbi: User = { age: 30, name: \"hello\"}\r\n\r\n\r\n// 함수 인자로의 활용\r\nconst getUser = (user:User){\r\nconsole.log(user);\r\n}\r\ngetUser({ age:10, name: \"hanbbi\" })\r\n\r\n\r\n\r\n// 함수 구조 활용\r\ninterface Sum {\r\n(a:number, b:number): number;\r\n}\r\n\r\nlet sumFinc: Sum:\r\nsumFunc = function(a: number, b: number): number {\r\nreturn a+b;\r\n}\r\n\r\n\r\n\r\n// 배열 활용\r\ninterface StringArray {\r\n[index:number]: string;\r\n}\r\n\r\nlet arr: StringArray = [\"a\", \"b\", \"c\"];\r\n\r\n\r\n// 객체 활용\r\ninterface StringRegexObject {\r\n[key: string]: RegExp;\r\n}\r\n\r\nconst obj: StringRegexObject {\r\ncssFile: /\\.css$/,\r\n jsFile: /\\.js$/\r\n}\r\n\r\n\r\n// interface 확장 (extends 사용)\r\ninterface Person {\r\nname: string;\r\nage:number;\r\n}\r\ninterface Developer extends Person {\r\nskill: string;\r\n}\r\n\r\nconst juniorDeveloper = {\r\nname: \"hanbbi\",\r\nage:100,\r\nskill: \"JS\"\r\n}\r\n\r\n```\r\n\r\n# 타입 별칭(type aliases)\r\n\r\n### 타입 키워드는 interface와 다르게 새로운 타입을 생성하는 것이 아닌 별칭을 부여하는 것으로, extends 키워드는 사용할 수 없음\r\n\r\n```typescript\r\n\r\n// 타입 별칭 선언 및 활용\r\ntype MyString = string;\r\nconst str: MyString = \"Hello dear\"\r\n\r\ntype Todo = {\r\nid: string;\r\ntitle: string;\r\ndone: boolean\r\n}\r\n\r\nconst getTodo(todo:Todo){\r\nconsole.log(todo);\r\n}\r\n\r\n```\r\n\r\n# 연산자 (Operator)\r\n\r\n## Uinon Type\r\n\r\n### 한 가지 이상의 type을 선언하고자 할 때 사용 가능. | 기호 사용\r\n\r\n```typescript\r\nconst logMessage = (value: string | number) => {\r\n if (typeof value === \"string\") {\r\n value.toString();\r\n } else if (typeof value === \"number\") {\r\n value.toLocaleString();\r\n } else {\r\n throw new TypeError(\"value must be string or number\");\r\n }\r\n};\r\nlogMessage(\"hello\");\r\nlogMessage(1000);\r\n```\r\n\r\n## intersection Type\r\n\r\n### 합집합과 같은 개념으로, 함수 호출의 경우 함수 인자에 명시한 type을 모두 제공해야 한다. & 기호 사용\r\n\r\n```typescript\r\n\r\ninterface Zoo {\r\nname: string;\r\nlocation: string\r\nprice: number;\r\n}\r\n\r\ninterface Animal {\r\nname: string;\r\ncount: number;\r\n}\r\n\r\nconst askZookeeper = ( value : Zoo & Animal ) => {\r\n // value 는\r\n { name:\"어린이대공원\", location: \"서울시 광진구\", price: 10000, count: 10000}\r\n // 와 같이 Zoo와 Animal이 모두 포함되는 인자를 줘야한다.\r\n}\r\n\r\n```\r\n\r\n# Enum\r\n\r\n### enum 키워드를 사용하면 일종의 default 값을 선언할 수 있다.\r\n\r\n```typescript\r\n\r\n// 숫자형 enum\r\n// 자동으로 0에서 1씩 증가하는 값을 부여\r\n\r\nenum Shoes {\r\nNike, // 0\r\nAdidas, // 1\r\nNewBalance //2\r\n}\r\nconst myShoes = Shoes.Nike; // 0\r\n\r\n문자형 enum\r\nenum Food {\r\ncake = \"케익\",\r\ncookie = \"쿠키\"\r\n}\r\nconst player = Food.cookie; // 쿠키\r\n\r\n```\r\n\r\n# 제네릭\r\n\r\n### 제네릭을 활용하면 인자를 넘겨 호출하는 시점에 타입을 결정할 수 있다. 제네릭 활용 시 동일한 기능을 하는 함수를 일일이 만들 필요가 없으며, 타입 추론에 있어 강점을 가진다.\r\n\r\n제네릭 선언\r\n와 같이 타입을 선언한다. 알파벳은 통상 T로 정해져 있다.\r\n\r\n```typescript\r\n\r\nconst logText = (text: T):T => {\r\nconsole.log(text);\r\nreturn text;\r\n}\r\nlogText(\"Hello hanbbi\");\r\n\r\n\r\n// interface에 제네릭 선언\r\n\r\ninterface Dropdown {\r\nvalue: T;\r\nselected: boolean;\r\n}\r\ncosnt obj: Dropdown = { value: \"hamburger\" , selected: true};\r\n\r\n```\r\n\r\n# 제네릭 타입 제한\r\n\r\n## 1. 배열 힌트\r\n\r\n```typescript\r\n\r\nconst logTextLength = (text: T[]): T[] =>{\r\nconsole.log(text.length);\r\ntext.forEach(text =>{\r\nconsole.log(text):\r\n});\r\n}\r\nlogTextLength([\"hi\", \"hello\"]);\r\n\r\n\r\n```\r\n\r\n## 2. 정의된 타입 이용(extends)과 keyof\r\n\r\n```typescript\r\n\r\ninterface ShoppingItem {\r\nname: string;\r\nprice: number;\r\nstock: number;\r\n}\r\n\r\nconst getShoppingItemOption(itemOption: T): T {\r\nreturn itemOption;\r\n}\r\n\r\n// \"name\", \"price\", \"stock\"만 인자로 가능\r\ngetShoppingItemOption(\"price\");\r\n\r\n```\r\n\r\n# 타입 추론 (Type inference)\r\n\r\n## 1. 기본 변수 타입 추론\r\n\r\n```typescript\r\n\r\n// string으로 추론\r\nlet a = \"abc\";\r\n\r\n// a: number로 추론\r\n// b: string으로 추론\r\n// return value는 string으로 추론\r\nconst getValue(a = 10) {\r\nlet b = \"hello\";\r\nreturn a + b;\r\n}\r\n\r\n\r\n```\r\n\r\n## 2. interface추론\r\n\r\n```typescript\r\n\r\n// interface 1개\r\ninterface Dropdown {\r\nvalue: T;\r\ntitle: string;\r\n}\r\nconst shoppingItem:Dropdown ={\r\nvalue: 10000;\r\ntitle: \"shoe\"\r\n}\r\n// interface 2개\r\ninterface Dropdown2 {\r\nvalue: T;\r\ntitle: string;\r\n}\r\ninterface DetailedDropdown extends Dropdown2{\r\ntag: K;\r\ndesc: string;\r\n}\r\nconst detailed: DetailedDropdown{\r\nvalue: \"10000\";\r\ntitle: \"shoe\",\r\ntag: \"10000\",\r\ndesc: \"description\"\r\n}\r\n\r\n```\r\n\r\n# 타입 단언 (Type assertion)\r\n\r\n### as 키워드를 사용해 타입을 정함으로써 typescript에게 타입을 알려줄 수 있다. 주로 DOM API를 조작할 떄 사용한다.\r\n\r\n```typescript\r\n// div가 있는지 장담할 수 없음, HTMLDivElement | null\r\n// 따라서 typescript에게 타입을 단언해 타입을 알려줄 수 있음.\r\nconst div = document.querySelector(\"div\") as HTMLDivElement;\r\ndiv.innerText = \"test\";\r\n```\r\n\r\n# 타입 가드 (Type guard)\r\n\r\n### union type을 사용하는 경우 공통된 속성만 접근 가능하며, 로직상 공통되지 않은 속성에 접근하고자 할 때 불편함을 해소하기 위해 타입 단언으로 공통되지 않은 속성에 접근하고자 하는 방법이 있지만, 코드 가독성을 위해 타입 가드 방법을 주로 사용한다.\r\n\r\n```typescript\r\nconst isDeveloper = (target: Developer | Humanoid): target is Developer => {\r\n return (target as Developer).skill !== undefined;\r\n};\r\nif (isDeveloper(tom)) {\r\n console.log(tom.name);\r\n console.log(tom.skill);\r\n} else {\r\n console.log(tom.name);\r\n console.log(tom.age);\r\n}\r\n```\r\n\r\n# 타입 호환 (Type compatibility)\r\n\r\n### TypeScript에서 더 큰 타입 구조를 갖는 변수에 작은 타입 구조를 갖는 변수를 할당 가능\r\n\r\n```typescript\r\nlet add = function (a: number) {\r\n // ...\r\n};\r\nlet sum = function (a: number, b: number) {\r\n // ...\r\n};\r\n// 아래는 에러가\r\n// add = sum;\r\n\r\n// 에러가 나지 않는 방식. sum의 구조가 더 크다고 볼 수 있음\r\nsum = add;\r\n```\r\n\r\n참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/\r\n"},{"title":"javascript how to use promise.all","description":"Promise.all()은 언제 쓰고 왜 쓰나요? - 여러 비동기 요청 한 번에 처리하기","category":"javascript","date":"2023-05-19","content":"\r\njavascript를 통해 비동기 코드를 처리하는 기본적인 방법으로는 Callback 함수, Promise, async/await 등이 있다. 하지만 다수의 비동기 요청의 실행이 필요한 경우 Promise.all()을 사용해볼 수 있다.\r\n\r\n## **Promise.all()이란?**\r\n\r\n여러 개의 Promise를 동시에 처리하고, 모든 Promise가 완료되었을 때 그 결과들을 반환하는 javascript의 메서드이다.\r\n\r\nPromise.all()은 배열형태의 Promise들을 인자로 받고, 이 Promise들은 병렬적으로 실행된다. Promise.all()은 해당 Promise들의 결과를 배열로 반환한다.\r\n\r\n**만약 하나라고 Promise가 거부되면, 첫 번째 거부된 Promise를 반환하고, 나머지 Promise들은 무시된다.**\r\n\r\n## **Promise.all()을 사용하는 이유**\r\n\r\nPromise.all()을 사용하면 병렬적으로 실행되는 Promise들을 효율적으로 관리하고, 모든 Promise의 결과를 한 번에 처리할 수 있다. 이는 비동기 작업을 동시에 처리하고 작업이 완료되었을 때 결과를 수집해야 하는 상황에서 유용하다.\r\n\r\n예를 들어 3초가 소요되는 비동기 요청 a, 2초가 소요되는 비동기 요청 b, 1초가 소요되는 비동기요청 c를 async/await을 통해 순차적으로 처리하게 되는 경우 총 6초가 소요되지만, Promise.all()을 통해 이전 요청이 완료되는 것을 기다리지 않고 비동기적으로 병렬로 실행시킨다면 3초만에 실행을 완료시킬 수 있는 것이다.\r\n\r\n```javascript\r\n\r\n// acyns/await\r\n\r\ncosnt test1 = async () =>{\r\n\tawait a() // 3000ms\r\n await b() // 2000ms\r\n await c() // 1000ms\r\n}\r\n// 약 6초 소요\r\n\r\nconst test2 = async () =>{\r\n\tconst [res1, res2, res3] = await Promise.all([\r\n \ta(),\r\n b(),\r\n c()\r\n ])\r\n return [res1, res2, res3]\r\n}\r\n// 약 3초 소요\r\n\r\n```\r\n\r\n## **Promise.all() 사용 상황**\r\n\r\n아래와 같이 특정 DB의 데이터를 연속적으로 도출하기 위해 async/await과 Promise.all()을 사용해봤다.\r\n\r\n```javascript\r\n\r\n// 1) async/await\r\nasync func1(){\r\n\tconst community_with_likes = await community.map(async (item) => {\r\n const like_list = await this.communityLikesRepository.find({\r\n where: {\r\n community_id: item.community_id,\r\n },\r\n });\r\n return like_list;\r\n });\r\n}\r\n\r\n// 2) Promise.all()\r\nasync func2(){\r\n\tconst community_with_likes = await Promise.all(\r\n community.map(async (item) => {\r\n const like_list = await this.communityLikesRepository.find({\r\n where: {\r\n community_id: item.community_id,\r\n },\r\n });\r\n return like_list;\r\n }),\r\n );\r\n}\r\n\r\n```\r\n\r\n### **1. async/await**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOMvFZ%2FbtsgB32dgj6%2FbaANEHQzX0hxTVckIjOPiK%2Fimg.png)\r\n\r\nasync/await을 사용해 결과를 출력하려고 할 때는 위와 같이 원하는 방식대로 데이터가 출력되지 않았다.\r\n\r\n### **2. Promise.all()**\r\n\r\n하지만 Promise.all()을 사용해 결과를 도출하니 아래와 같이 원하는 결과값을 볼 수 있었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo73VZ%2FbtsgCGeohNN%2F42YW4sTfKAiXcl3VeJgP51%2Fimg.png)\r\n\r\n비동기 처리를 다루는 방법은 여러가지가 있지만, 내가 적용하고자 하는 작업의 특성에 맞게 알맞는 방법을 골라서 사용하는 것이 좋겠다.\r\n\r\n---\r\n\r\n참조\r\n\r\nhttps://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all\r\n\r\nhttps://code-masterjung.tistory.com/91\r\n"},{"title":"nestjs localhost https","description":"Localhost 환경에서 HTTPS 적용하기","category":"nestjs","date":"2023-04-30","content":"\r\n### Local 환경에서 https 설정이 필요한 이유\r\n\r\n로컬 환경에서 내가 원하는대로 기능들이 작동한다고 해도, 배포 환경과 로컬 개발 환경의 차이로 인해 코드의 수정이 필요해질 수 있다. 이러한 이유로 개발 환경을 배포 환경과 최대한 동일하게 만들어주는 것이 좋다. 또한 최근의 배포 환경들은 대부분 https로 이뤄져 있기 때문에 그에 맞춰 설정해줄 필요가 있다.\r\n\r\n### 자체 서명 인증서 생성 (openssl 사용)\r\n\r\n```bash\r\n# 개인 키 생성\r\n$ openssl genrsa -out private-key.pem 2048\r\n\r\n# 개인 키를 사용한 새로운 인증서 요청서 생성\r\n$ openssl req -new -key private-key.pem -out cert-request.csr\r\n\r\n# 요청서를 사용한 자체 서명 인증서 생성\r\n$ openssl x509 -req -in cert-request.csr -signkey private-key.pem -out cert.pem\r\n```\r\n\r\n인증서 생성 시 입력사항은 모두 건너뛰어도 무관하다.\r\n\r\n### HTTPS 구성 설정 - main.ts\r\n\r\n```typescript\r\nimport * as fs from \"fs\";\r\nimport * as https from \"https\";\r\n\r\nasync function bootstrap() {\r\n const httpsOptions = {\r\n key: fs.readFileSync(\"./private-key.pem\"),\r\n cert: fs.readFileSync(\"./cert.pem\"),\r\n };\r\n const app = await NestFactory.create(AppModule, {\r\n httpsOptions,\r\n });\r\n // 필요에 따라 cors 설정도 가능\r\n // app.enableCors({\r\n // origin: [url,...],\r\n // credentials: true, 쿠키를 사용하는 경우 설정\r\n // });\r\n await app.listen(3000);\r\n}\r\nbootstrap();\r\n```\r\n\r\n설정이 완료되면 로컬에서 https 환경처럼 적용할 수 있다.\r\n\r\n하지만 자체 인증서로 https를 만들어 SSR(ServerSideRendering)이나 API 요청 시 self certifi 에러가 발생할 수 있다. 이런 경우 프론트단 env에 다음과 같이 설정해주면 된다.\r\n\r\n```bash\r\n# .env\r\nNODE_TLS_REJECT_UNAUTHORIZED=0\r\n```\r\n\r\n그럼에도 오류가 발생하는 경우 아래의 링크를 통해 크롬 환경설정을 해주면 된다.\r\n\r\nchrome://flags/#allow-insecure-localhost\r\n\r\n유효하지 않은 인증서 허용으로 설정\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3WChm%2Fbtsdi3DwSL4%2F5cdQ9YFzgaKugT4u9ewDk0%2Fimg.png)\r\n"},{"title":"nestjs server client cookie2","description":"NestJS에서 클라이언트로 쿠키 보내기 (왜 내 쿠키는 안받아줘요?) - 배포환경","category":"nestjs","date":"2023-04-30","content":"\r\n### **[서버에서 브라우저로 쿠키 저장](https://lee-yo-han.github.io/nestjs-server-client-cookie)**\r\n\r\n분명 로컬에서는 쿠키가 정상적으로 브라우저에 저장되는 것을 볼 수 있었는데, 배포를 하고 나니 또다시 쿠키가 보이지 않았다. response headers에는 쿠키 값과 함께 다음과 같은 에러 문구를 볼 수 있었다.\r\n\r\n> **this attempt to set a cookie via a set cookie header was blocked because its domain attribue was invalid with reqards to the current host url**\r\n>\r\n> > \"설정된 쿠키 헤더를 통해 쿠키를 설정하려는 이 시도는 해당 도메인 속성이 현재 호스트 URL에 대해 유효하지 않기 때문에 차단되었습니다.\"\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGZzIY%2FbtsdemcKoT1%2FcT16wBLzk5J48gv37fGyPK%2Fimg.png)\r\n\r\n쿠키 전송 옵션은 다음과 같다.\r\n\r\n```typescript\r\nres.cookie(\"cookie\", \"cookie\", {\r\n maxAge: 0,\r\n sameSite: process.env.COOKIE_PARSE_SAME_SITE as SameSite, // \"none\"\r\n secure: true,\r\n httpOnly: true,\r\n domain: process.env.COOKIE_PARSE_DOMAIN, // \"www.backend.com\"\r\n});\r\n```\r\n\r\n프론트엔드의 URL이 https://www.frontend.com 이고,\r\n\r\n백엔드의 URL이 https://www.backend.com 이라고 했을 때, domain 속성을 백엔드 URL로 설정했을 때 발생한 오류이며, domain을 프론트엔드 URL로 바꾸면 response headers에 쿠키 값 자체가 확인되지 않았다.\r\n\r\ncors 설정도 credential: true나 origin 등을 정상적으로 해놓은 상태이기 때문에 쿠키 외 다른 데이터는 정상적으로 받아오는걸 확인할 수 있었다.\r\n\r\n다른 자료를 찾아보니, secure 속성을 적용하면 쿠키가 정상적으로 저장된다고는 하지만 본인은 이런 문제가 해결되지 않았다.\r\n\r\n결국 도메인을 구매해서 사용하게 되었다.\r\n\r\n예를 들어 프론트엔드의 URL은 https://www.product.com 로,\r\n\r\n백엔드의 URL은 https://api.product.com 으로 연동시켜주고, 쿠키 옵션은 아래처럼 바꿨다.\r\n\r\n```typescript\r\nres.cookie(\"cookie\", \"cookie\", {\r\n maxAge: 0,\r\n sameSite: process.env.COOKIE_PARSE_SAME_SITE as SameSite, // \"lax\"\r\n secure: true,\r\n httpOnly: true,\r\n domain: process.env.COOKIE_PARSE_DOMAIN, // \".product.com\"\r\n});\r\n```\r\n\r\n같은 도메인을 사용하고 나서야 쿠키가 정상적으로 브라우저로 접속하는 것을 확인할 수 있었다.\r\n"},{"title":"nextjs pages api dir unsupported","description":"NextJS에서 API 디렉토리가 사용되지 않는 이유","category":"nextjs","date":"2023-04-30","content":"\r\nNextJS는 pages/api 디렉토리를 통해 백엔드에 대한 처리를 수행할 수 있다.\r\n\r\n하지만 정적으로 배포된 사이트(ex. 블로그 등)에서는 pages/api 등과 같은 동적 로직은 지원되지 않기 때문에 api 디렉토리를 사용할 수 없다.\r\n\r\n정적 배포 방법\r\n\r\n```javascript\r\n// package.json\r\n// NextJS 13.3 이전 버전\r\n\"scripts\": {\r\n \"dev\": \"next dev\",\r\n \"start\": \"next start\",\r\n \"lint\": \"next lint\",\r\n \"build\": \"next build && next export\", // ## 정적 배포\r\n \"predeploy\": \"npm run build\",\r\n \"deploy\": \"touch out/.nojekyll && gh-pages -d out --dotfiles\"\r\n },\r\n\r\n\r\n// next.config.js\r\n// NextJS v13.3 ~\r\n/**\r\n * @type {import('next').NextConfig}\r\n */\r\nconst nextConfig = {\r\n output: 'export',\r\n}\r\n\r\nmodule.exports = nextConfig\r\n```\r\n\r\n---\r\n\r\n참조\r\n\r\nhttps://nextjs.org/docs/advanced-features/static-html-export\r\n"},{"title":"nextjs prop classname did not match","description":"prop `classname` did not match. with styled-components","category":"nextjs","date":"2023-04-30","content":"\r\n## prop 'classname' did not match. with styled-components 에러 해결 방법\r\n\r\nNextJS의 next.config를 수정해준다.\r\n\r\n```javascript\r\n/** @type {import('next').NextConfig} */\r\nconst nextConfig = {\r\n reactStrictMode: false,\r\n compiler: {\r\n styledComponents: true, // 컴파일러 옵션 추가\r\n },\r\n};\r\n\r\nmodule.exports = nextConfig;\r\n```\r\n"},{"title":"nestjs server client cookie","description":"NestJS에서 클라이언트로 쿠키 보내기 (왜 내 쿠키는 안받아줘요?)","category":"nestjs","date":"2023-04-13","content":"\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpD9qr%2Fbtr9Oa0APIT%2FFuzZyjjney7aKkIXy6BAfK%2Fimg.png)\r\n\r\n## 쿠키(Cookie)란?\r\n\r\n쿠키는 서버에서 클라이언트에게 보내는 작은 데이터 조각이다. 일반적으로 브라우저에서 웹 사이트를 방문할 때 쿠키를 사용하여 사용자의 활동을 기록하고, 이후에 그 사용자가 같은 웹 사이트를 방문할 때 이 정보를 사용해 그에 맞게 동작하게 해준다. 하지만 쿠키는 당사자뿐만 아닌 제 3자가 조회하는 것도 가능하기 때문에 개인 정보를 담는 등 보안상 민감한 정보를 저장하는 데에는 적합하지 않기 때문에 탈취되거나 사용자에 의해 조작되어도 크게 문제 되지 않을 정보를 주로 저장한다. (ex. 다크 모드, 장바구니 목록 등)\r\n\r\n## 서버에서 클라이언트로 Cookie 전송 예제\r\n\r\n### 모듈 설치\r\n\r\n```bash\r\nyarn add cookie-parser @types/cookie-parser # 쿠키 관리 모듈\r\n```\r\n\r\n### 모듈 적용\r\n\r\n```typescript\r\n// main.ts\r\n\r\nimport { NestFactory } from \"@nestjs/core\";\r\nimport { AppModule } from \"./app.module\";\r\nimport * as cookieParser from \"cookie-parser\";\r\n\r\nasync function bootstrap() {\r\n const app = await NestFactory.create(AppModule);\r\n app.enableCors({\r\n // cors 설정\r\n origin: \"http://localhost:3000\",\r\n credentials: true, // 쿠키를 사용할 수 있게 해당 값을 true로 설정\r\n });\r\n app.use(cookieParser()); // 쿠키의 편리한 이용을 위해 cookieParser 적용\r\n await app.listen(3001);\r\n}\r\nbootstrap();\r\n```\r\n\r\n### 쿠키 사용 로직\r\n\r\n```typescript\r\n// controller.ts\r\n\r\nimport { Response } from \"express\";\r\nimport { Controller, Get, Res } from \"@nestjs/common\";\r\n\r\n@Controller(\"myController\")\r\nexport class MyController {\r\n @Get()\r\n getCookie(@Res() res: Response) {\r\n // express의 Response 객체를 불러와 사용해준다.\r\n // 쿠키 설정\r\n res.cookie(\"cookieName\", \"cookieValue\", { maxAge: 900000, httpOnly: true });\r\n // maxAge : 유효기간을 밀리초 단위로 설정\r\n // httpOnly : 클라이언트에서 쿠키에 접근할 수 없도록 설정\r\n\r\n // HTTP 응답 반환\r\n return res.send(\"쿠키가 설정되었습니다.\");\r\n }\r\n}\r\n```\r\n\r\n쿠키를 요청하는 클라이언트에서도 withCredentials 값을 true로 설정해준다.\r\n\r\n```typescript\r\naxios.get(BASE_URL, {\r\n withCredentials: true,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n },\r\n});\r\n```\r\n\r\n## 문제상황\r\n\r\n보통 예제는 위와 같지만 나는 쿠키가 브라우저에 저장되지 않았다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd0P7QS%2Fbtr9SjoI9ke%2FIW9CJf8K5mS7kDuau67KDK%2Fimg.png)\r\n\r\n로직이 잘못된 것처럼 보이지도 않았고 별도의 에러 메시지도 없어서 문제 파악이 어려웠다.\r\n\r\n네트워크 탭을 통해 확인해 보니 response 헤더에 쿠키도 정상적으로 들어가 있었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDRJgW%2Fbtr9NnsBfTs%2FlGo4A2uw6n2UXPJJQVwbb1%2Fimg.png)\r\n\r\n그런데 노란 경고판이 눈에 띄었다.\r\n\r\n경고 문구는 다음과 같다.\r\n\r\n> **this set-cookie header didn't specify a samesite attribute, was defaulted to sameSite=Lax, and was blocked because it came from a cross-site reponse which was not the response to a top-level navigation. this response is considered cross-site because the URL has a different scheme than the current site**\r\n\r\n요청 URL과 서버 URL의 체계가 달라서 차단됐다고 한다. 내 클라이언트는 http://localhost인데 서버가 https://localhost라서 그런 것 같았다.\r\n\r\nsameSite를 none으로 설정해주고 난 후에는 다음과 같은 경고 문구가 생겼다.\r\n\r\n> **this attempt to set a cookie via a set-cookie header was blocked because it had the \"SameSite=None\" attribute but did not have the \"Secure\" attibute, which is required in order to user \"sameSite=None\"**\r\n\r\nsameSite=none을 적용하기 위해 필요한 Secure 속성이 없기 때문에 쿠키 설정이 차단되었단다.\r\n\r\n쿠키 생성 옵션을 마저 추가해 줬다.\r\n\r\n```typescript\r\nres.cookie(\"cookieName\", \"cookieValue\", {\r\n maxAge: 300000,\r\n // none, lax, strict 중 none은 쿠키가 항상 전송되도록 허용.\r\n sameSite: \"none\", // HTTPS 프로토콜을 사용하고 secure 옵션이 설정된 경우에만 사용 가능\r\n secure: true, // 쿠키가 HTTPS 프로토콜을 사용하는 경우에만 전송되도록 제한\r\n httpOnly: true, // 쿠키에 접근할 수 있는 영역을 HTTP(S) 프로토콜로 제한하여,\r\n // 브라우저의 자바스크립트 코드로부터 쿠키에 접근할 수 없게 함\r\n});\r\n```\r\n\r\nsecure 속성을 사용하기 위한 https 설정은 아래를 참고할 수 있다.\r\n\r\n### [Localhost 환경에서 https 적용하기](https://lee-yo-han.github.io/nestjs-localhost-https)\r\n\r\n그 결과 쿠키를 브라우저에서 잘 받는 것을 볼 수 있었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJwJs6%2Fbtr9SkupB08%2FbXf0Hf9swCaY3uL8cxMh7K%2Fimg.png)\r\n\r\n마지막으로 서버에서 쿠키를 받아 사용하는 방법이다.\r\n\r\n```typescript\r\nimport { Response, Request } from \"express\";\r\nimport { Controller, Get, Post, Res, Req } from \"@nestjs/common\";\r\n\r\n@Controller(\"myController\")\r\nexport class MyController {\r\n @Get()\r\n getCookie(@Res() res: Response) {\r\n // express의 Response 객체를 불러와 사용해준다.\r\n // 쿠키 설정\r\n res.cookie(\"cookieName\", \"cookieValue\", { maxAge: 900000, httpOnly: true });\r\n // maxAge : 유효기간을 밀리초 단위로 설정\r\n // httpOnly : 클라이언트에서 쿠키에 접근할 수 없도록 설정\r\n\r\n // HTTP 응답 반환\r\n return res.send(\"쿠키가 설정되었습니다.\");\r\n }\r\n\r\n @Get()\r\n postCookie(@Req() req: Request) {\r\n // express의 Request 객체를 불러와 사용해준다.\r\n // request 객체의 cookies를 통해 생성했던 쿠키의 이름을 통해 값을 가져온다.\r\n req.cookies[\"cookieName\"];\r\n }\r\n}\r\n```\r\n\r\n클라이언트 단에서는 withCredentials:true 옵션 설정 외에는 특별히 해줄 것이 없다.\r\n\r\n---\r\n\r\n참조 : https://docs.nestjs.com/techniques/cookies\r\n"},{"title":"nestjs use bcrypt","description":"Nestjs에서 bcrypt 사용하기","category":"nestjs","date":"2023-04-13","content":"\r\n## bycrypt란?\r\n\r\n비밀번호 등을 안전하게 암호화하여 저장하고 검증할 수 있도록 도와주는 라이브러리로, 랜덤한 salt를 생성하고 이를 비밀번호와 함께 암호화하여 저장한다. 이후 비밀번호 검증 시에도 같은 salt를 사용하여 입력받은 비밀번호를 암호화한 후, 저장된 암호화된 비밀번호와 비교하여 일치하는지 검증한다.\r\n\r\nbcrypt는 암호화 강도를 선택할 수 있는 기능을 제공한다. 기본값은 10으로, 숫자가 클수록 강도가 높아지며 암호화에 소요되는 시간도 늘어난다. 보안을 위해 기본값 이상의 값을 권장한다.\r\n\r\n## 사용법\r\n\r\n```bash\r\nyarn add bcrypt @types/bcrypt # nestjs는 타입스크립트가 기본이기 때문에 타입으로 설치\r\n```\r\n\r\n따로 module이나 main에 등록할 필요 없이 사용하고자 하는 파일에 불러오면 된다.\r\n\r\n```typescript\r\nimport * as bcrypt from 'bcrypt';\r\n\r\n// 암호화 후 저장\r\nconst someFN = async (userData:UserData) => {\r\n\tconst hashPassword = await bcrypt.hash(userData.password,10)\r\n\tconst saveData = {\r\n \temail:userData.email,\r\n password: hashPassword,\r\n }\r\n // 정보 저장 로직\r\n}\r\n\r\n// 저장된 데이터를 통해 비밀번호 확인\r\nconst checkFN = async (userData:UserData) =>{\r\n\tconst hashedPassword = // 저장된 정보 불러오는 로직\r\n\r\n // true or false\r\n const match = await bcrypt.compare(userData.password,hashedPassword)\r\n if(match) // 인증 후 로직\r\n}\r\n```\r\n\r\n---\r\n\r\n참조 : https://github.com/kelektiv/node.bcrypt.js#readme\r\n"},{"title":"react common components","description":"React 공통 컴포넌트 제작 (input, button)","category":"react","date":"2023-04-13","content":"\r\n## 공통 컴포넌트 제작의 필요성\r\n\r\n> 하나의 사이트를 제작할때 비슷한 input이나 button을 사용해야 하는 상황이 자주 있는데, 공통된 컴포넌트를 만들어 놓으면 코드 중복을 줄이면서 상황에 맞춰 적절하게 사용할 수 있게 된다.\r\n\r\n## 컴포넌트 제작\r\n\r\n### Input\r\n\r\n```typescript\r\nimport React from \"react\"; // element 속성을 가져오기 위해 import\r\nimport styled from \"styled-components\"; // CSS 적용\r\n\r\n// 컴포넌트의 props 타입을 InputElement의 속성과 내가 설정하고자 하는 타입을 포함한다.\r\ntype Props = React.HTMLAttributes & InputProps;\r\n\r\ninterface InputProps {\r\n type?: string | undefined; // type?: 의 ?는 있어도 되고 없어도 된다는 의미\r\n name?: string | undefined;\r\n autoComplete?: string | undefined;\r\n width: string;\r\n height: string;\r\n border?: string | undefined;\r\n}\r\n\r\n// width와 height는 number로 지정해도 무관하나,\r\n// 상황에 따라 뷰포트에 맞춰 쓸지, px에 맞춰 쓸지 달라질 수 있기 때문에 string으로 설정한 경우\r\n\r\nexport const MainInput = ({\r\n type,\r\n name,\r\n autoComplete,\r\n width,\r\n height,\r\n border,\r\n ...props\r\n}: Props) => {\r\n return (\r\n \r\n );\r\n};\r\n\r\nconst Main = styled.input`\r\n padding: 5px;\r\n border: none;\r\n border: 1px solid ${props => props.theme.inputBorderColor};\r\n border-radius: 5px;\r\n font-size: 1.2rem;\r\n &:hover {\r\n background: ${props => props.theme.inputBorderColor};\r\n }\r\n &:focus {\r\n outline: none;\r\n border-color: ${props => props.theme.hoverBorderColor};\r\n box-shadow: 0 0 1px ${props => props.theme.hoverBorderColor};\r\n }\r\n`;\r\n```\r\n\r\n### TextArea\r\n\r\n```typescript\r\ntype TaProps = React.HTMLAttributes & TextAreaProps;\r\ninterface TextAreaProps {\r\n name?: string | undefined;\r\n width: string;\r\n height: string;\r\n border?: string | undefined;\r\n}\r\n\r\nexport const MainTextArea = ({\r\n name,\r\n width,\r\n height,\r\n border,\r\n ...props\r\n}: TaProps) => {\r\n return (\r\n \r\n );\r\n};\r\n\r\nconst TextArea = styled.textarea`\r\n padding: 5px;\r\n border: none;\r\n border: 1px solid ${props => props.theme.inputBorderColor};\r\n border-radius: 5px;\r\n font-size: 1.2rem;\r\n resize: none;\r\n white-space: pre;\r\n &:hover {\r\n background: ${props => props.theme.inputBorderColor};\r\n }\r\n &:focus {\r\n outline: none;\r\n border-color: ${props => props.theme.hoverBorderColor};\r\n box-shadow: 0 0 1px ${props => props.theme.hoverBorderColor};\r\n }\r\n`;\r\n```\r\n\r\n## Button\r\n\r\n```typescript\r\ntype Props = React.HTMLAttributes & ButtonProps;\r\n\r\ninterface ButtonProps {\r\n type?: \"button\" | \"submit\" | \"reset\" | undefined; // 버튼 타입은 이와 같이 설정해준다.\r\n content: string;\r\n width: string;\r\n height: string;\r\n}\r\n\r\nexport const MainButton = ({\r\n type,\r\n content,\r\n width,\r\n height,\r\n ...props\r\n}: Props) => {\r\n return (\r\n \r\n {content}\r\n \r\n );\r\n};\r\n\r\nconst Main = styled.button`\r\n border: none;\r\n border-radius: 5px;\r\n background: none;\r\n background-color: ${props => props.theme.mainButton};\r\n color: ${props => props.theme.mainFontColor};\r\n font-size: 1rem;\r\n font-weight: bold;\r\n cursor: pointer;\r\n &:hover {\r\n background-color: ${props => props.theme.mainButtonHover};\r\n }\r\n`;\r\n```\r\n"},{"title":"react type assertion","description":"React Form event type (feat. 타입 단언 as)","category":"react","date":"2023-04-13","content":"\r\n## Form Event Type\r\n\r\n우리는 로그인이나 포스팅 등의 화면을 만들어줄 때 클라이언트가 입력하는 데이터를 입력받는 방법 중 하나로 onChange 등의 FormEvent를 이용해 원하는 값을 추출할 수 있다. 이때 이벤트 타입은 아래와 같이 사용될 수 있다.\r\n\r\n```typescript\r\nimport { FormEvent } from \"react\";\r\n\r\nexport type FormEvents = FormEvent;\r\nexport type InputEvent = FormEvent;\r\nexport type LabelEvent = FormEvent;\r\n```\r\n\r\nreact에서 FormEvent 타입을 받아오고, 해당되는 element를 제네릭 타입으로 넣어주면 된다.\r\n\r\n## onChangeHandler\r\n\r\n```typescript\r\nconst [formData, setFormData] = useState(formDataInit);\r\nconst formChangeHandler = (e: FormEvent) => {\r\n const target = e.target;\r\n const name = target.name;\r\n const value = target.value;\r\n setFormData(prev => ({ ...prev, [name]: value }));\r\n};\r\n```\r\n\r\n원하는 데이터를 받을 수는 있지만, name과 value의 타입 에러가 발생한다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F80Q0s%2Fbtr9SOPHzuW%2F7t6EmYi1k5gMiyeurxmCP0%2Fimg.png)\r\n\r\ntarget의 타입을 정해줘도 타입 에러가 발생하는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbsb5ww%2Fbtr9N2aq2VJ%2FsFJkJduLoSt1bhyae9SFdK%2Fimg.png)\r\n\r\n하지만 우리는 form의 input이 변할때마다 대상의 값을 받아오는 것을 알고 있다.\r\n\r\n이때 필요한 것이 타입 단언(as)이다.\r\n\r\n## 타입 단언 사용 예시\r\n\r\n```typescript\r\nconst formChangeHandler = (e: FormEvents) => {\r\n const target = e.target as InputTarget; // InputTarget === HTMLInputElement\r\n const name = target.name;\r\n const value = target.value;\r\n setFormData(prev => ({ ...prev, [name]: value }));\r\n};\r\n```\r\n\r\n### 주의사항\r\n\r\n타입 단언을 사용하면 타입 체크를 할 수 없다. 사실상 강제로 타입을 지정하는 행위이기 때문에 타입 체커에게 해당 타입 에러를 무시하라는 명령이나 다름없다. as를 남발하거나 내려받는 데이터가 확실하지 않는 경우에는 오류 탐색에 지장이 있을 수 있으니, 로직에 따라 조건문 등을 사용해 타입 에러를 피해 주는 것이 좋을 수 있다.\r\n\r\n```typescript\r\n// ex)\r\nconst someFN = (someData) =>{\r\n\tconst someMutation = someData..logic\r\n if(someMutation){\r\n \t// ... nest logic\r\n }\r\n}\r\n```\r\n"},{"title":"css use download font","description":"CSS - 배포 사이트에 다운로드 폰트 적용하기","category":"css","date":"2023-03-31","content":"\r\n> 기본적으로 CSS에서 제공하는 폰트가 마음에 들지 않거나 내 프로젝트에 어울리지 않는 경우 간단하게 다운로드한 폰트를 프로젝트에 적용해볼 수 있다.\r\n\r\n## 1. 폰트 다운로드\r\n\r\n우선 마음에 드는 폰트를 다운받는다.\r\n\r\n카페24무료폰트: https://fonts.cafe24.com/\r\n\r\n무료 폰트 사이트는 찾아보면 사용할 수 있는게 많다.\r\n\r\n## 2. 프로젝트에 파일 복사\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHA9Qr%2Fbtr60vtCf7O%2FMaaAsK1Lg4t5QDXd3bC1Uk%2Fimg.png)\r\n\r\n위와 같이 다운로드한 폰트를 작업중인 프로젝트에 넣어준다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA7Inw%2Fbtr61haATVa%2F9mFQHIOUCbjYI7SnPm8wI0%2Fimg.png)\r\n\r\n필자는 styles/fonts/\\*\\*.ttf 와 같이 저장해줬다.\r\n\r\n## 3. CSS 설정\r\n\r\n@font -face{}를 이용해 불러올 폰트를 설정해준다.\r\n\r\n```css\r\n/* global.css */\r\n\r\n@font-face {\r\n font-family: maplestory;\r\n src: url(\"./fonts/Maplestory\\ Light.ttf\");\r\n}\r\n@font-face {\r\n font-family: maplestoryBold;\r\n src: url(\"./fonts/Maplestory\\ Bold.ttf\");\r\n}\r\n```\r\n\r\n## 4. CSS 사용\r\n\r\n평소에 폰트를 적용하는 것처럼 CSS를 적용해주면 된다.\r\n\r\n```css\r\nbody {\r\n font-family: maplestory;\r\n}\r\n```\r\n"},{"title":"github markdown syntex","description":"Markdown 문법 정리","category":"github","date":"2023-03-31","content":"\r\n## 1. headers\r\n\r\n\\# 으로 시작하는 텍스트로 1-6개 가능 (h1 ~ h6)\r\n\r\n```\r\n# h1\r\n## h2\r\n### h3\r\n#### h4\r\n##### h5\r\n###### h6\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIOzmy%2Fbtr7d8qRKov%2FiXZSIKWKSOiAtliDzN63vK%2Fimg.png)\r\n\r\n## 2. 구분선\r\n\r\n\"---\"이나 \"\\*\\*\\*\"를 통해 구분선 생성 가능\r\n\r\n```\r\n***\r\n---\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgcFby%2Fbtr7dx5ZjTh%2FlEqZkNRdg7rLooCPmMFZW0%2Fimg.png)\r\n\r\n## 3. 줄바꿈\r\n\r\n\"
\"\r\n\r\n```\r\n줄을
바꿈\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8z1Q4%2Fbtr7fs3s9fI%2FhLPfWRYRfKMIC2tUvU8531%2Fimg.png)\r\n\r\n## 4. 강조\r\n\r\n기울여쓰기(italic) : \\* 또는 \\_로 감싼 텍스트.\r\n\r\n두껍게 쓰기(bold) : \\*\\* 또는 \\_\\_로 감싼 텍스트.\r\n\r\n취소선 : ~~로 감싼 텍스트\r\n\r\n```\r\n_기울여쓰기 1_\r\n*기울여쓰기 2*\r\n**두껍게 1**\r\n__두껍게 2__\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu2sXJ%2Fbtr7dNgeNxn%2FcCfQQ9PumZ7OzrH2KeXq5K%2Fimg.png)\r\n\r\n## 5. 인용\r\n\r\n\\>로 시작하는 텍스트로 >>>와 같이 3개까지 사용 가능\r\n\r\n```\r\n> 인용문\r\n> > 3개까지\r\n> > > 사용 가능\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGU0Me%2Fbtr7ghUUjee%2FqgoUnVkdOOkK7Kb5Gq1oPK%2Fimg.png)\r\n\r\n## 6. 리스트\r\n\r\n리스트는 Tab을 통해 목록 안의 목록 생성 가능\r\n\r\n### 순서가 없는 목록\r\n\r\n\\*, +, -를 통해 순서가 없는 목록 생성 가능\r\n\r\n```\r\n- 순서가 없는 목록1\r\n- 순서가 없는 목록1\r\n- 순서가 없는 목록1\r\n\r\n* 순서가 없는 목록2\r\n - 탭을 이용한 목록 안의 목록\r\n - 탭을 이용한 목록 안의 목록 안의 목록\r\n\r\n1. 순서가 있는 목록1\r\n2. 순서가 있는 목록1\r\n3. 순서가 있는 목록1\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQp7i8%2Fbtr7e1ygv06%2FvMIjB3OsI8Q5fAteCTJbF1%2Fimg.png)\r\n\r\n### 순서가 있는 목록\r\n\r\n숫자를 기입하면 순서가 있는 목록이 됨\r\n\r\n들여쓰기를 하면 모양이 바뀜\r\n\r\n숫자를 무엇을 쓰든 순서대로 알아서 숫자를 매김\r\n\r\n리스트 안에 하위 리스트를 만들기 위해서는 tab과 함께 숫자 1번부터 나열하면 적용 가능\r\n\r\n```\r\n1. 순서가 있는 목록2\r\n2. 순서가 있는 목록2\r\n 1. 탭을 이용한 순서가 있는 목록 안의 목록\r\n 1. 탭을 이용한 순서가 있는 목록 안의 목록 안의 목록\r\n 2. 탭을 이용한 순서가 있는 목록 안의 목록\r\n5. 순서가 있는 목록2\r\n```\r\n\r\n혼합 리스트도 적용 가능\r\n\r\n```\r\n1. 순서가 있는 목록2\r\n2. 순서가 있는 목록2\r\n 1. 탭을 이용한 순서가 있는 목록 안의 목록\r\n * 탭을 이용한 순서가 있는 목록 안의 목록 안의 순서가 없는 목록\r\n 2. 탭을 이용한 순서가 있는 목록 안의 목록\r\n5. 순서가 있는 목록2\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlhYHi%2Fbtr7dPd52Gh%2FY87O8eblzYL1LiuEiIdMik%2Fimg.png)\r\n\r\n## 7. 이미지\r\n\r\n링크와 비슷하나 앞에 느낌표가 붙음\r\n\r\n인라인 이미지 : ![텍스트](이미지파일 경로)\r\n\r\n![image](\"./../../../../public/images/cards/GITHUB.png)\r\n\r\n링크 이미지 : ![텍스트] (이미지파일URL)\r\n\r\n![image](https://img1.daumcdn.net/thumb/C428x428/?scode=mtistory2&fname=https%3A%2F%2Ftistory1.daumcdn.net%2Ftistory%2F5148697%2Fattach%2Fc60c2213f3984f0b9da48413e3fa277e)\r\n\r\n이미지 파일에 마우스를 올렸을 때 커서 앞에 나오는 텍스트 설정\r\n\r\n![텍스트] (이미지경로/URL \"이미지이름\")\r\n\r\n![image](https://img1.daumcdn.net/thumb/C428x428/?scode=mtistory2&fname=https%3A%2F%2Ftistory1.daumcdn.net%2Ftistory%2F5148697%2Fattach%2Fc60c2213f3984f0b9da48413e3fa277e \"이미지지롱\")\r\n\r\n링크와 이미지를 합친 문법 (이미지를 링크로 사용)\r\n\r\n[![텍스트] (이미지URL)] (링크URL)\r\n\r\n[![image](https://img1.daumcdn.net/thumb/C428x428/?scode=mtistory2&fname=https%3A%2F%2Ftistory1.daumcdn.net%2Ftistory%2F5148697%2Fattach%2Fc60c2213f3984f0b9da48413e3fa277e)](https://lee-yo-han.github.io/github-token-expired)\r\n\r\n## 8. Link (Anchor)\r\n\r\n글자로 된 하이퍼링크\r\n\r\n```\r\n[구글] (링크)\r\n```\r\n\r\n[구글](https://www.google.com/)\r\n\r\n내부(해시) 링크\r\n\r\n[보여지는 내용] (#이동할 헤드(제목))\r\n\r\n괄호 안 링크의 띄어쓰기는 -로 연결, 영어는 모두 소문자로 작성\r\n\r\n```\r\n[어디로든문](#1-headers)\r\n```\r\n\r\n[어디로든문](#1-headers)\r\n\r\n## 9. 코드 블럭\r\n\r\n백틱(`) 3개씩으로 감싸서 사용\r\n\r\n```\r\n'''javascript\r\nsome code ...\r\n'''\r\n```\r\n\r\n## 10. 테이블\r\n\r\n헤더와 셀 구분 시 3개 이상의 하이픈(-) 필요\r\n헤더 셀을 구분하면서 콜론(:)으로 정렬 가능\r\n가장 좌측과 가장 우측에 있는 vertical bar( | ) 기호 생략 가능\r\n\r\n```\r\n| 헤더1 | 헤더2 | 헤더3 | 헤더4 |\r\n| ----- | :------- | :--------: | -------: |\r\n| 셀1 | 셀2 | 셀3 | 셀4 |\r\n| 기본 | 좌로정렬 | 가운데정렬 | 우로정렬 |\r\n| 셀9 | 셀10 | 셀11 | 셀12 |\r\n```\r\n\r\n| 헤더1 | 헤더2 | 헤더3 | 헤더4 |\r\n| ----- | :------- | :--------: | -------: |\r\n| 셀1 | 셀2 | 셀3 | 셀4 |\r\n| 기본 | 좌로정렬 | 가운데정렬 | 우로정렬 |\r\n| 셀9 | 셀10 | 셀11 | 셀12 |\r\n\r\n---\r\n\r\n참조 https://inpa.tistory.com/entry/MarkDown-%F0%9F%93%9A-%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4-%EB%AC%B8%EB%B2%95-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC\r\n"},{"title":"github token expired","description":"Github Token을 이용한 GithubAPI 사용","category":"github","date":"2023-03-31","content":"\r\n### **본 포스팅은 NextJS 13 환경에서 진행한 내용을 바탕으로 한다. GithubAPI 사용법은 공식 문서에 상세히 나와있기 때문에 사용법보다 토큰 만료 문제를 주로 다룬다.**\r\n\r\n## GithubAPI란?\r\n\r\n> Github의 기능을 REST API 형식으로 사용할 수 있도록 도와주는 기능이다.\r\n\r\n**[GithubAPI docs](https://docs.github.com/en/rest?apiVersion=2022-11-28)**\r\n\r\n아래와 같이 사용할 수 있는 기능이 많다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6rpoI%2Fbtr65pd5Q0W%2F7NUKUqiXklINKYLwJWPow1%2Fimg.png)\r\n\r\n## 1. 토큰 발급\r\n\r\n간단한 조회 정도는 토큰이 필요하지 않지만, 요청의 종류에 따라 토큰이 필요한 경우가 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS8fhz%2Fbtr60iAL28F%2FxJ66qxT7PKF4U6JjLkCA91%2Fimg.png)\r\n\r\n우선 우측 상단의 Settings로 들어가서\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdU186G%2Fbtr61k5X0vE%2FdrgRV6uJMfho9JCIhoUghk%2Fimg.png)\r\n\r\nSettings의 좌측 하단 Developer settings에 들어간다.\r\n\r\n그 안에 Pat(personal access tokens)를 발급받을 수 있는 창을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE3TPa%2Fbtr60DxUYrw%2FtWMeaxCI78Cyx9838COCVK%2Fimg.png)\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnE2jt%2Fbtr60gbWaov%2FQ897sC2Gk2RxKlsBVZ5jLK%2Fimg.png)\r\n\r\nGenerate new token을 통해 원하는 종류의 토큰을 받을 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHPjvr%2Fbtr61gW4V3n%2Fvqk7jPvdQAcdlAfXomGiQ0%2Fimg.png)\r\n\r\n토큰명, 유효기간, access할 repo를 설정하고\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX6MQh%2Fbtr61lcFYwt%2F1fkFl327Q0I91UIwAgfCfK%2Fimg.png)\r\n\r\n원하는 기능에 대한 권한을 설정해준다.\r\n\r\n이번엔 간단하게 issue에 대한 작업만 진행해본다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVESxa%2Fbtr63YA3qrw%2FCedCRRISe4SG6PLHK4H0dK%2Fimg.png)\r\n\r\n생성된 토큰은 잘 복사해 안전한 공간에 저장해준다.\r\n\r\n## 2. Token 사용\r\n\r\n기본적인 사용법은 아래와 같다.\r\n\r\nhttps://docs.github.com/en/rest/guides/getting-started-with-the-rest-api?apiVersion=2022-11-28\r\n\r\n```typescript\r\n// issues 등록하기\r\nimport { Octokit } from \"octokit\";\r\n\r\nconst octokit = new Octokit({\r\n auth: `api token`,\r\n});\r\n\r\nconst issueUpdate = async (name: string, feed: string) => {\r\n await octokit.request(\r\n \"POST /repos/{owner}/{repository name}/issues/1/comments\",\r\n {\r\n owner: \"user name\",\r\n repo: \"repository name\",\r\n title: `title`,\r\n body: `body`,\r\n // headers: {\r\n // \"X-GitHub-Api-Version\": \"2022-11-28\",\r\n // },\r\n },\r\n );\r\n};\r\n```\r\n\r\n위 요청을 실행하면 정상적으로 issue 등록이 되는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjcLkP%2Fbtr65o7kXPZ%2Fh739Lzn25eBdRhDzcPPU8K%2Fimg.png)\r\n\r\n## 유의할 점\r\n\r\n### 1. headers 설정\r\n\r\n우선 headers를 주석처리한 이유는 공식 문서에서는 github API version을 headers에 담으라고 돼있지만, 버전과 함께 요청을 실행하면 cors에러가 발생하는 것을 볼 수 있다.\r\n\r\n**[Github CORS ISSUE](https://github.com/orgs/community/discussions/40619)**\r\n\r\nheaders는 별도로 추가하지 말고 auth만 설정해준 후 사용해 주도록 한다.\r\n\r\n### 2. auth Token 설정\r\n\r\nAPI key와 같은 토큰은 대부분 .env를 통해 변수를 설정해 사용해줘야 한다.\r\n\r\n하지만 .env에 토큰을 설정하고 commit을 하게되면 gitignore를 설정해줘도 github가 자동으로 감지해 토큰을 만료시키기 때문에 .env.local서 테스트하고 .env의 토큰은 지운 뒤에 사용해주자.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuJQsS%2Fbtr62Xh6jLO%2FHkR7hA4k0cMffBKY55DHu1%2Fimg.png)\r\n\r\n---\r\n\r\n참조 : https://docs.github.com/en\r\n"},{"title":"react smooth scroll","description":"React/Javascript 부드러운 스크롤 이동 적용","category":"react","date":"2023-03-25","content":"\r\n# **부드러운 스크롤 이동 적용 방법**\r\n\r\n> behavior:\"smooth\" 속성을 scroll 이벤트와 scroll 메서드와 같이 사용하며 부드러운 스크롤 이동을 적용해볼 수 있다.\r\n\r\n본 보스팅은 Nextjs 13 환경에서 작성되었다.\r\n\r\n## **scroll() 메서드**\r\n\r\n### **scroll() , scrollTo()**\r\n\r\nscroll(x,y) || scroll(options), scrollTo(x,y) || scrollTo(options) 와 같이 사용할 수 있다.\r\n\r\n사실상 두 메서드는 동일하게 작동한다.\r\n\r\nxy 좌표를 통해서도 사용 가능하며 options를 적용한다면 아래와 같이 사용할 수 있다.\r\n\r\n```javascript\r\nwindow.scroll({\r\n top: 0,\r\n left: 0,\r\n behavior: \"smooth\", // smooth: 부드럽게 전환 , auto: 즉시 이동\r\n});\r\n\r\nwindow.scrollTo({\r\n top: 0,\r\n left: 0,\r\n behavior: \"smooth\", // smooth: 부드럽게 전환 , auto: 즉시 이동\r\n});\r\n```\r\n\r\n### **scrollBy()**\r\n\r\n**scrollBy(x,y) || scrollBy(options)**\r\n\r\nscroll()과 scrollTo()가 특정 좌표로 이동한다면, scrollBy는 길이만큼 스크롤을 이동시킨다.\r\n\r\n옵션과 예제는 아래와 같다.\r\n\r\n```javascript\r\n// 기본 사용\r\nwindow.scrollBy(x, y);\r\n\r\n// 한 페이지 아래\r\nwindow.scrollBy(0, window.innerHeight);\r\n\r\n// 한 페이지 위\r\nwindow.scrollBy(0, -window.innerHeight);\r\n\r\n// 옵션\r\nwindow.scrollBy({\r\n top: 0,\r\n left: 0,\r\n behavior: \"smooth\", // smooth: 부드럽게 전환 , auto: 즉시 이동\r\n});\r\n```\r\n\r\nscroll 메서드가 적용된 화면과 예제코드\r\n\r\n![image](https://blog.kakaocdn.net/dn/bV3l2O/btr5OcgXBcx/ickRekJIOy3VMmRLtzt2pK/img.gif)\r\n\r\n```javascript\r\n// 최상단으로 이동\r\nconst moveScroll = () => {\r\n scroll({ top: 0, behavior: \"smooth\" });\r\n};\r\n\r\n// 한 페이지 위로 이동\r\nconst moveScrollByUp = () => {\r\n scrollBy({ top: -window.innerHeight, behavior: \"smooth\" });\r\n};\r\n\r\n// 한 페이지 아래로 이동\r\nconst moveScrollByDown = () => {\r\n scrollBy({ top: window.innerHeight, behavior: \"smooth\" });\r\n};\r\n\r\n// top 500 위치로 이동\r\nconst moveScrollTo = () => {\r\n scrollTo({\r\n top: 500,\r\n behavior: \"smooth\",\r\n });\r\n};\r\n\r\n// jsx\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n;\r\n```\r\n\r\n## **Chrome 브라우저에서 behavior:\"smooth\" 속성이 적용되지 않을 때**\r\n\r\n아래와 같이 크롬 버전은 최신이지만, smooth 속성이 적용되지 않는 현상이 나타났다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx3Fal%2Fbtr5ORwXdp3%2FAHsT7DB2D5i76OjhYqcN9k%2Fimg.png)\r\n\r\n![image](https://blog.kakaocdn.net/dn/bourLn/btr5NyrsLbU/nkulo83V1NSn2kEMQb2xYK/img.gif)\r\n\r\n호환성에도 문제가 없지만 MDN의 예제를 실행해 봐도 크롬에선 smooth 속성이 사용되지 않았다.\r\n\r\nMDN 스크롤 예제 : https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmJ1Dl%2Fbtr5Nv2OZvD%2Fmk5c6Hks8ai7J4UKhL6wR1%2Fimg.png)\r\n\r\n크롬 브라우저 설정을 따로 해주면 해결된다고는 하지만, 내 PC에서만 작동하는 건 근본적인 해결방법이 아니기 때문에 배제했다.\r\n\r\n참조 : http://www.devdic.com/css/refer/properties/property:1195/scroll-behavior\r\n\r\n### **smoothscroll-polyfill - 적용 실패**\r\n\r\n사용법 : https://www.npmjs.com/package/smoothscroll-polyfill?activeTab=readme\r\n\r\n```typescript\r\n// 패키지 설치\r\n// yarn add smoothscroll-polyfill\r\n\r\n// 최상단 파일\r\n\r\nimport smoothscroll from \"smoothscroll-polyfill\";\r\n\r\nif (typeof window !== \"undefined\") {\r\n smoothscroll.polyfill();\r\n}\r\n```\r\n\r\n브라우저 호환성 적용을 위한 패키지라고 하는데, 본인에겐 효과가 없었다.\r\n\r\n## **scroll 이벤트를 통한 스크롤 이동 적용**\r\n\r\n스크롤만 이용해서 기능을 적용할 생각이었기에 직접 만들기로 했다.\r\n\r\naddEventListener를 통해 스크롤 이벤트를 감지해 주는 hook을 만들어줬다.\r\n\r\n```typescript\r\n// 스크롤 위 아래 여부 확인\r\nimport { useState, useEffect, useRef } from \"react\";\r\n\r\ninterface MutableRefObject {\r\n current: T;\r\n}\r\n\r\nexport const useScroll = () => {\r\n const [scrollCheck, setScrollCheck] = useState(\"\");\r\n const prevScrollPos: MutableRefObject = useRef(0);\r\n\r\n useEffect(() => {\r\n const handleScroll = () => {\r\n const currentScrollPos = window.pageYOffset; // window객체의 pageYOffset 를 통해 현재 스크롤 위치 저장\r\n if (currentScrollPos > prevScrollPos.current) {\r\n // 스크롤 위치에 따른 스크롤 진행 방향 검증\r\n setScrollCheck(\"DOWN\");\r\n } else {\r\n setScrollCheck(\"UP\");\r\n }\r\n prevScrollPos.current = currentScrollPos; // 사용된 현재 스크롤 위치를 다음 이벤트 비교대상으로 저장\r\n };\r\n\r\n window.addEventListener(\"scroll\", handleScroll); // event 등록\r\n return () => {\r\n window.removeEventListener(\"scroll\", handleScroll); // event clear\r\n };\r\n }, [prevScrollPos]);\r\n return {\r\n scrollCheck, // up/down 여부 return\r\n };\r\n};\r\n\r\n// hook 사용 컴포넌트\r\nimport { useScroll } from \"@/hooks/useScroll\";\r\nconst { scrollCheck } = useScroll();\r\nuseEffect(() => {\r\n if (scrollCheck === \"UP\") {\r\n console.log(\"scroll UP\");\r\n }\r\n if (scrollCheck === \"DOWN\") {\r\n console.log(\"scroll DOWN\");\r\n }\r\n}, [scrollCheck]);\r\n```\r\n\r\n원하는 값이 잘 나오는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF7CNJ%2Fbtr5QzP3p9N%2FErlbv8julxnc1PKZSGRC71%2Fimg.png)\r\n\r\n> 스크롤 이벤트 특성상 한 번 휠을 돌릴 때마다 많은 호출이 일어나기 때문에 필요한 경우에만 사용될 수 있도록 setTimeout 등을 통해 적용하고자 하는 코드에 맞게 함수 실행을 조절해줘야 한다.\r\n\r\n```typescript\r\nimport { useState, useEffect, useRef } from \"react\";\r\nimport { scrollToSmoothly } from \"@/components/profile/SmoothScroll\";\r\nimport { useScroll } from \"@/hooks/useScroll\";\r\nconst { scrollCheck } = useScroll();\r\nconst scrollRef = useRef({\r\n // window.innerHeight 값 저장\r\n height: 0,\r\n});\r\n\r\nconst smoothScrollHandler = () => {\r\n let maxHeight = window.innerHeight * 6; // 최대 스크롤 길이\r\n let currentHeight = scrollRef.current.height; // 현재 스크롤 저장\r\n if (scrollCheck === \"UP\" && currentHeight !== 0) {\r\n // scroll up이고 맨 위가 아닐 때\r\n scrollRef.current.height -= window.innerHeight; // useRef에 이동할 height 조정\r\n scrollToSmoothly(scrollRef.current.height, 500); // 이동할 height까지 500ms동안 이동시킬 함수 (requestAnimationFrame 사용)\r\n } else if (scrollCheck === \"DOWN\" && currentHeight !== maxHeight) {\r\n // scroll이 down이고 맨 아래가 아닐 때\r\n scrollRef.current.height += window.innerHeight; // 상기 동일\r\n scrollToSmoothly(scrollRef.current.height, 500);\r\n }\r\n};\r\n\r\nuseEffect(() => {\r\n smoothScrollHandler();\r\n}, [scrollCheck]); // useScroll()을 통해 구분한 UP/Down이 변경될 때마다 실행\r\n```\r\n\r\n위의 함수를 실행하면 아래와 같이 그나마 정상 작동하는 모습을 볼 수 있다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/cpwxY9/btr5NJNGHOP/34qTHrvGprHknp2jmkpkpK/img.gif)\r\n\r\n## **a 태그를 통한 스크롤 이동**\r\n\r\na 태그의 href 속성을 이용해 간단하게 스크롤을 이동시키는 방법도 있다.\r\n\r\n```javascript\r\n\r\n \r\n 1번박스로 이동 // nextjs에서 Link는 a태그의 역할을\r\n 한다.\r\n 2번박스로 이동 // href에 스크롤을 이동시킬 'ID'를 입력하고\r\n 클릭하면\r\n 3번박스로 이동 // 해당 ID요소로 스크롤을\r\n 이동시킨다.\r\n 4번박스로 이동\r\n 5번박스로 이동\r\n 6번박스로 이동\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n```\r\n\r\n---\r\n\r\n**참조**\r\n\r\n**scroll 메서드**\r\n\r\nhttps://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior\r\n\r\nhttps://developer.mozilla.org/en-US/docs/Web/API/Window/scroll\r\n\r\n**requestAnimationFrame**\r\n\r\nhttps://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame\r\n\r\nhttps://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp\r\n"},{"title":"react component lifecycle","description":"React 함수형 컴포넌트와 Class형 컴포넌트 생명주기","category":"react","date":"2023-03-23","content":"\r\n# **React state와 LifeCycle**\r\n\r\n> 함수형 컴포넌트와 class형 컴포넌트의 LifeCycle이 어떻게 되는지 간단하게 비교해 보는 글이다.\r\n\r\n## 클래스형 컴포넌트와 생명주기 메서드\r\n\r\n### 1. Mount(컴포넌트가 처음 실행될 때)\r\n\r\n- state, context, defalutProps 저장\r\n- componentWillMount - 안전하지 않은 접근\r\n- render\r\n- componentDidMount - DOM 접근 가능\r\n\r\n### 2. Props Update(프롭스가 업데이트될때)\r\n\r\n- componentWillReceiveProps - 사용 종료\r\n- shouldComponentUpdate\r\n- componentWillUpdate - 사용 종료\r\n- render\r\n- componentDidUpdate - DOM 접근 가능\r\n\r\n### 3. State Update (스테이트가 업데이트됐을 때)\r\n\r\n- shouldComponentUpdate\r\n- componentWillUpdate - 사용 종료\r\n- render\r\n- componentDidUpdate - DOM 접근 가능\r\n\r\n**사실상 componentWillReceiveProps와 componentWillUpdate의 사용 종료로 state와 props가 업데이트될 때 동일하게 작동한다.**\r\n\r\n### 4. Unmount (컴포넌트가 제거되는 것)\r\n\r\n- componentWillUnmount\r\n\r\n사용 종료의 기준은 아래와 같다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0qQTJ%2Fbtr5h96VtyB%2FqLpFZAk7aybC21CEWPD9H1%2Fimg.png)\r\n\r\n## 함수형 컴포넌트와 useEffect 훅\r\n\r\n### 컴포넌트의 실행\r\n\r\n- 함수형 컴포넌트 호출\r\n\r\n- 함수형 컴포넌트의 내부에서 실행\r\n\r\n- return()으로 화면에 렌더링\r\n\r\n- 생명주기 메서드 대신 useEffect를 통한 비슷한 처리 가능\r\n\r\n### dependency에 따른 useEffect의 실행\r\n\r\n- deps 값이 없는 경우 : 화면이 렌더링 된 이후 수행이 되며, 리렌더링이 발생할 때마다 다시 실행\r\n\r\n- deps 값이 빈 배열인 경우 : 첫 렌더링 완료 후 1회만 실행\r\n\r\n- deps 값이 존재하는 경우 : 첫 렌더링 완료 후 1회 실행 && deps 값이 변경되었을 경우마다 실행\r\n\r\n```javascript\r\n// dep X\r\nuseEffect(() => {\r\n // effect\r\n return () => {\r\n // cleanup\r\n };\r\n});\r\n\r\n// dep []\r\nuseEffect(() => {\r\n return () => {};\r\n}, []);\r\n\r\n// dep [some dep...]\r\nuseEffect(() => {\r\n return () => {};\r\n}, [dep]);\r\n```\r\n\r\n**[Mounting]** useEffect() - 컴포넌트 렌더링 이후 실행\r\n\r\n- dep 설정에 따라 실행됨\r\n\r\n**[Updating]** useEffect() - 컴포넌트 내에서 변화가 발생했을 경우 실행\r\n\r\n- 부모 컴포넌트의 리렌더링, 부모로부터의 props값 변화, 해당 컴포넌트 내에서 state 변경 등\r\n\r\n**[Unmounting]** useEffect() - 컴포넌트 내에서 DOM을 제거할 때 실행되는 메서드\r\n\r\n- 컴포넌트의 DOM이 제거될 때 수행되며 useEffect 내부의 return 값이 사용됨\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbryvuq%2Fbtr5pFcEsKC%2FIUUp45qfkl3kWGMGsXLoF0%2Fimg.png)\r\n\r\n**위와 같이 컴포넌트를 여닫는 페이지가 있다고 가정했을 때, console을 통해 대략적인 흐름을 확인해 볼 수 있다.**\r\n\r\n### **Class형 컴포넌트**\r\n\r\n**컴포넌트가 처음 실행될 때**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOvMHN%2Fbtr5nGi34Pf%2Fr7cGOBhis0thuYoKgE5Y81%2Fimg.png)\r\n\r\n**state가 변경될 때 (props가 변경될 때와 동일)**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD45Ra%2Fbtr5gtxPbHC%2FBcyreT9Rmknfdukr6PuUMK%2Fimg.png)\r\n\r\n**컴포넌트가 제거될 때**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6mDAw%2Fbtr5qsKSrcJ%2FSzo6Rjkm0liLDy3wCZZ27k%2Fimg.png)\r\n\r\n### **함수형 컴포넌트**\r\n\r\n**컴포넌트가 처음 실행될 때**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3fXKy%2Fbtr5paDZhii%2FxGLf5vByXzPMT1jphHzBkK%2Fimg.png)\r\n\r\n**컴포넌트가 업데이트될 때**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7vKwD%2Fbtr5paYhomj%2F5iPCpcpuRTIKbpr0dFNNO1%2Fimg.png)\r\n\r\n> 콘솔에 출력된 것과 같이, useEffect의 return 이후 부분은 componentWillUnmount와 비슷하지만, 컴포넌트가 다시 렌더링 되기 전마다 다시 실행된다.\r\n\r\n**컴포넌트가 제거될 때**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5feUj%2Fbtr5sdzGrHx%2F1FTXMaZBnR6FmKQmslJRK1%2Fimg.png)\r\n\r\n컴포넌트가 제거될 때는 componentWillUnmount처럼 useEffect의 return 부분이 실행되는 것을 볼 수 있다.\r\n\r\n사용된 코드\r\n\r\n```javascript\r\n// Lifecycle.jsx\r\n\r\nimport { useState } from \"react\";\r\nimport { FunctionComponent } from \"./FunctionComponent\";\r\nimport { ClassComponent } from \"./ClassComponent\";\r\n\r\nexport const Lifecycle = () => {\r\n const [fnOpen, setFnOpen] = useState(false);\r\n const [classOpen, setClassOpen] = useState(false);\r\n const effectComponentHandler = num => {\r\n if (num) {\r\n setFnOpen(!fnOpen);\r\n } else {\r\n setClassOpen(!classOpen);\r\n }\r\n };\r\n\r\n return (\r\n
\r\n

Fn component / Class component

\r\n \r\n \r\n {fnOpen ? : null}\r\n {classOpen ? : null}\r\n
\r\n );\r\n};\r\n```\r\n\r\n```javascript\r\n// FunctionComponent.jsx\r\n\r\nimport { useState, useEffect } from \"react\";\r\n\r\nexport const FunctionComponent = props => {\r\n console.log(\"컴포넌트 렌더링\", props);\r\n const [count, setCount] = useState(0);\r\n useEffect(() => {\r\n // effect\r\n if (count === 0) {\r\n console.log(`useEffect 첫 등장`);\r\n } else {\r\n console.log(`useEffect 다시 등장 count: ${count}`);\r\n }\r\n return () => {\r\n // cleanup\r\n console.log(\r\n \"useEffect 퇴장 --- componentWillUnmount와 비슷하지만 리렌더링마다 사용됨\",\r\n );\r\n };\r\n }, [count]);\r\n\r\n const plus = () => {\r\n setCount(prev => prev + 1);\r\n };\r\n const minus = () => {\r\n setCount(prev => prev - 1);\r\n };\r\n\r\n return (\r\n
\r\n

함수형 컴포넌트

\r\n {count}\r\n \r\n \r\n

Props 받기

\r\n
props1 = {props.some1}
\r\n
props2 = {props.some2}
\r\n
props3 = {props.some3}
\r\n
\r\n );\r\n};\r\n```\r\n\r\n```javascript\r\n// ClassComponent.jsx\r\n\r\nimport { Component } from \"react\";\r\n\r\nexport class ClassComponent extends Component {\r\n constructor(props) {\r\n super(props);\r\n // Class형 컴포넌트의 state는 무조건 객체 형태여야 한다.\r\n this.state = {\r\n counter: 0,\r\n };\r\n console.log(\r\n \"Mount - 컴포넌트가 처음 실행될 때 state, context, defalutProps 저장\",\r\n props,\r\n );\r\n }\r\n plus = () => {\r\n this.setState(state => ({ counter: state.counter + 1 }));\r\n };\r\n minus = () => {\r\n this.setState(state => ({ counter: state.counter - 1 }));\r\n };\r\n\r\n componentWillMount() {\r\n console.log(\"componentWillMount\");\r\n }\r\n componentDidMount() {\r\n console.log(\"componentDidMount - DOM 접근 가능\");\r\n }\r\n componentDidUpdate() {\r\n console.log(\"componentDidUpdate\");\r\n }\r\n shouldComponentUpdate() {\r\n console.log(\"shouldComponentUpdate - state or props 업데이트\");\r\n return true;\r\n }\r\n componentWillUnmount() {\r\n console.log(\"componentWillUnmount\");\r\n }\r\n\r\n render() {\r\n return (\r\n
\r\n

Class형 컴포넌트

\r\n {this.state.counter}\r\n \r\n \r\n

Props 받기

\r\n
props1 = {this.props.some1}
\r\n
props2 = {this.props.some2}
\r\n
props3 = {this.props.some3}
\r\n
\r\n );\r\n }\r\n}\r\n```\r\n\r\n---\r\n\r\n참조\r\n\r\nhttps://ko.reactjs.org/docs/state-and-lifecycle.html\r\n\r\nhttps://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html\r\n"},{"title":"javascript dynamic import","description":"Dynamic import를 사용해 동적으로 모듈 가져오기","category":"javascript","date":"2023-03-18","content":"\r\n# Dynamic import란?\r\n\r\n> import() 표현식으로 사용하며, 표현식은 모듈을 읽고 해당 모듈이 내보내는 것들을 모두 포함하는 객체를 담은 이행된 Promise를 반환한다. 호출은 어디서나 가능하다.\r\n\r\n# Dynamic import를 사용하는 이유\r\n\r\n- 기존 import문은 정적인 방식으로, 정적으로 가져오는 경우 코드 로드 속도가 느려지고, 가져오는 코드가 필요할 가능성이 적거나 없을 수 있다.\r\n- 정적으로 가져올 때 프로그램의 메모리 사용량이 크게 증가하고 가져오는 코드가 필요할 가능성이 낮다.\r\n- import문에 동적 매개변수를 사용할 수 없다.\r\n\r\n> 즉, 동적으로 import하는 것은 성능 향상과 필요한 경우에 맞춰 사용하는 것에 용이하다.\r\n\r\n## 사용법\r\n\r\n필요한 함수들을 정의하고, 필요한 곳에서 import하여 원하는 방식대로 적용해준다.\r\n\r\n예시 코드\r\n\r\n```javascript\r\n// someFn.js\r\n\r\nexport const greeting = () => {\r\n console.log(\"어서오시고\");\r\n};\r\n\r\nexport const importMe = () => {\r\n console.log(\"다이나믹 임포트를 해주세요\");\r\n};\r\n\r\nexport const easy = () => {\r\n console.log(\"쉽죠?\");\r\n};\r\n\r\nexport const add = (a, b) => console.log(a + b);\r\n```\r\n\r\n```javascript\r\n// use import()\r\nexport const DynamicImport = () => {\r\n // async / await 방식\r\n const loadGreeting = async () => {\r\n const DI = await import(\"./someFn.js\");\r\n DI.greeting();\r\n };\r\n const loadImportMe = async () => {\r\n const DI = await import(\"./someFn.js\");\r\n\r\n DI.importMe();\r\n };\r\n const loadEasy = async () => {\r\n const DI = await import(\"./someFn.js\");\r\n DI.easy();\r\n };\r\n\r\n const loadAddAwait = async (a, b) => {\r\n const DI = await import(\"./someFn.js\");\r\n return DI.add(a, b);\r\n };\r\n // 기본 방식\r\n const loadAdd = (a, b) => {\r\n const DI = import(\"./someFn.js\")\r\n .then(module => {\r\n module.add(a, b);\r\n })\r\n .catch(err => err);\r\n return DI;\r\n };\r\n\r\n return (\r\n
\r\n

DynamicImport component

\r\n \r\n \r\n \r\n \r\n \r\n
\r\n );\r\n};\r\n```\r\n\r\n## Promise를 반환하기 때문에 async/await을 통한 사용도 가능하다.\r\n\r\n위 코드를 통해 생성한 버튼들을 모두 한 번씩 클릭해주면 아래와 같이 사용되는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyOVoT%2Fbtr4v801voG%2FmX38iUM5zpLCGKZ5nkyWYK%2Fimg.png)\r\n\r\n> 참고사항 : Dynamic import는 일반 스크립트에서도 동작하기 때문에 script type=\"module\"이 없어도 된다.\r\n\r\n### 주의사항 : import()는 함수 호출과 문법이 유사해 보이지만 함수 호출이 아니다. super()처럼 괄호를 사용하는 특별한 문법 중 하나이다. 따라서 import를 변수에 복사하거나 call/apply 등의 사용은 불가능하다.\r\n\r\n---\r\n\r\n참조\r\n\r\nMDN : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import\r\n\r\nModern js : https://ko.javascript.info/modules-dynamic-imports\r\n"},{"title":"javascript jest test code","description":"jest를 통한 테스트코드 사용","category":"javascript","date":"2023-03-18","content":"\r\n# jest란?\r\n\r\n> 단순성에 중점을 둔 javascript 테스트 프레임워크로, babel, Typescript, node, React, Angular, Vue 등을 사용하는 프로젝트에서 작동한다.\r\n\r\n본 포스팅은 React 환경을 기준으로 작성되었다.\r\n\r\n## 1. jest 설치\r\n\r\n```bash\r\nnpm install --save-dev jest @babel/preset-env\r\n# or\r\nyarn add --dev jest @babel/preset-env\r\n```\r\n\r\nnode.js 환경에서 import 등의 ES6 문법을 사용하기 위해 @babel/preset-env도 함께 설치해준다.\r\n\r\n## 2. 루트 경로에 babel.config.js 파일 생성\r\n\r\n```javascript\r\nmodule.exports = {\r\n presets: [\r\n \"@babel/preset-env\",\r\n // typescript를 사용하는 경우 yarn add --dev @babel/preset-typescript 설치 후\r\n // 아래와 같이 preset을 추가해준다.\r\n // \"@babel/preset-typescript\"\r\n ],\r\n};\r\n```\r\n\r\nbabel config 파일까지 생성이 완료됐다면 test 파일을 사용할 준비가 다 된 것이다.\r\n\r\n## 3. jest가 인식하는 test file 이름\r\n\r\n- {filename}.test.js\r\n- {filename}.spec.js\r\n\r\n## 4. 테스트 실행\r\n\r\njest 커맨드만으로도 테스트 코드를 진행할 수 있지만, 다른 플래그와 함께 사용할 수 있다.\r\n\r\n```bash\r\n--coverage # 프로젝트의 테스트 커버리지를 함께 나타내준다.\r\n--watch # 대상 코드나 테스트 코드에 변경이 생기면 테스트를 다시 실행한다.\r\n\r\n# ex) yarn jest --watch --coverage\r\n```\r\n\r\njest만 실행한 터미널\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmEHBz%2Fbtr4t9fB6IJ%2Fl1JqzgbkjoBxDREUdoSgzK%2Fimg.png)\r\n\r\n--coverage와 함께 실행한 터미널\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPTj6f%2Fbtr4vjvmeEt%2Ft5NIYNk6kzRJ4PQ1IQA48k%2Fimg.png)\r\n\r\n> 참고 : jest는 테스트 코드 내에서 오류가 발생할 때만 테스트를 fail시키기 때문에 test(\"\",()=>{})와 같이 테스트 코드가 비어있더라도 테스트를 통과할 수 있다.\r\n\r\n## 5. 테스트 코드 패턴\r\n\r\n### AAA 패턴 (Arrange - Act - Assert)\r\n\r\n- A 조건에서 B를 실행했을때 예상한 결과가 C가 나오는가? 라는 흐름의 패턴이다.\r\n\r\n예제\r\n\r\n```javascript\r\n// 기존 함수\r\nexport const add = (a, b) => {\r\n return a + b;\r\n};\r\n\r\n// somefn.spec.js - 테스트코드 함수\r\n\r\nimport { add } from \"./someFn\";\r\n\r\ntest(\"add 함수 테스트: result = a+b\", () => {\r\n // 조건\r\n const a = 2;\r\n const b = 3;\r\n const result = 5;\r\n\r\n // 실행\r\n const actResult = add(a, b);\r\n\r\n // 평가\r\n expect(actResult).toBe(result);\r\n});\r\n```\r\n\r\n평가 부분의 코드는 .toBe()라는 matcher 함수를 통해 actResult를 실행했을 때 result가 나올 것이라는 기대를 갖고 코드를 평가하게 된다.\r\n\r\n아래와 같이 여러 함수를 한 테스트 코드 안에 넣을 수도 있지만, 리팩토링 시 불편한 경험을 할 수 있기 때문에 상황에 맞게 사용해주는 것이 좋을 것 같다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckHjWD%2Fbtr4t5Eptqn%2FccX4fWxWW6GZ9edDMtjdAK%2Fimg.png)\r\n\r\n주요 matcher 함수\r\n\r\n- toBe() : 원시형 데이터 비교\r\n- toEqual() : 참조형 데이터를 깊은 비교를 통해 비교\r\n- toBeTruthy() : 값이 참인지 검사\r\n- toContain() : 특정 요소가 배열 안에 존재하는지 검사\r\n- not : 다른 matcher 함수 앞에 체이닝해 사용하며, 해당 값의 반대값이 참인지 검사 (not은 함수가 아닌 property)\r\n\r\n공식 문서에서 더 자세하게 확인해볼 수 있다. (https://jestjs.io/docs/using-matchers)\r\n\r\n## 6. 테스트 코드를 통해 할 수 있는 것\r\n\r\n- 내가 작성한 함수들이 내가 원하는대로 작동하는지 확인할 수 있다.\r\n- 테스트 코드 작성을 통해 TDD 방식의 개발 방법론을 적용할 수 있다.\r\n\r\n## TDD란?\r\n\r\n> TDD는 테스트 주도 개발(Test Driven Development)을 뜻하며, 테스트 케이스를 우선적으로 설계해 개발을 진행하는 방법론이다.\r\n\r\n### 1. 테스트 케이스를 우선적으로 설계하고,\r\n\r\n### 2. 함수를 테스트 케이스에 맞춰 제작하며,\r\n\r\n### 3. 로직을 개선하며 1~3을 반복한다.\r\n\r\n즉, 실패-성공-리팩토링의 반복으로 볼 수 있다.\r\n\r\nTDD의 예시\r\n\r\nhttps://velog.io/@velopert/TDD%EC%9D%98-%EC%86%8C%EA%B0%9C\r\n\r\n---\r\n\r\n참조 : https://jestjs.io/\r\n"},{"title":"nextjs link userouter","description":"Nextjs Link와 useRouter의 차이","category":"nextjs","date":"2023-03-18","content":"\r\n# Link / useRouter 사용법\r\n\r\n```javascript\r\n// Link\r\nimport Link from \"next/link\";\r\n\r\nexport const compo1 = () => {\r\n return (\r\n \r\n
some element
\r\n \r\n );\r\n};\r\n\r\n// useRouter\r\nimport { useRouter } from \"next/router\";\r\n\r\nexport const compo2 = () => {\r\n const router = useRouter();\r\n\r\n const routeHandler = () => {\r\n router.push(url);\r\n };\r\n\r\n return (\r\n
\r\n some element\r\n
\r\n );\r\n};\r\n```\r\n\r\n### Nextjs에서 페이지를 전환하기 위해 사용하는 방법으로, Link를 이용하거나 useRouter를 이용해 router.push()와 같이 사용하여 전환해줄 수 있는데, 비슷해 보이는 두 가지 방법의 차이를 간단히 정리해본다.\r\n\r\n## Link의 특징\r\n\r\n- Link는 Client-side navigation으로, javascript로 페이지 전환이 이뤄진다.\r\n- 기본 navigation보다 빠르며 SPA(Single Page Application)의 특성을 유지한다.\r\n\r\n## useRouter의 특징\r\n\r\n- react-router-dom의 history.push()와 유사하다.\r\n- 크롤러가 링크를 감지하지 못해 SEO가 좋지 않을 수 있다.\r\n- 외부 URL을 사용할 경우 window.location 혹은 a 태그를 사용해야 한다.\r\n\r\n또한 Link는 클릭 시 바로 페이지가 전환되지만, useRouter는 로직을 처리한 후 원하는 시점에 전환이 가능하다.\r\n\r\n## 결론\r\n\r\n> router.push()는 onClick에 사용되는 행동(action)이기 때문에 Link 태그보다 검색에 불리하지만, 로직의 처리에 따라 활용도가 높다.\r\n\r\n> Nextjs의 장점인 SEO(Search Engine Optimization - 검색 엔진 최적화)를 원한다면 Link를 사용하는 것이 더 유리하다.\r\n\r\n---\r\n\r\n참조\r\n\r\nhttps://stackoverflow.com/questions/65086108/next-js-link-vs-router-push-vs-a-tag\r\n\r\nhttps://nextjs.org/docs/api-reference/next/link\r\n\r\nhttps://nextjs.org/docs/api-reference/next/router\r\n"},{"title":"nextjs marked webpack imported module 7 default","description":"WEBPACK_IMPORTED_MODULE_7_default is not a function 에러 해결","category":"nextjs","date":"2023-03-18","content":"\r\n# 에러 해결 방법\r\n\r\n나의 경우 SSR 데이터를 contextAPI를 통해 패치하려 했으나, 정상적으로 받아오지 못할때 발생한 오류였다.\r\n\r\n로컬 실행 종료 후 재실행하니 정상적으로 데이터를 받아올 수 있었다.\r\n\r\n다소 허탈한 해결방안이었지만, SSR을 다루는 페이지의 데이터가 정상적으로 받아와지지 않는 경우, 코드 작성 후 프로그램을 다시 실행해보자.\r\n\r\n---\r\n\r\n참조 : https://github.com/vercel/next.js/issues/18090\r\n"},{"title":"react memo","description":"React.memo() 를 통한 컴포넌트 최적화","category":"react","date":"2023-03-16","content":"\r\n> 유저에게 UI를 빠르게 제공하기 위해서는 컴포넌트의 렌더링을 최소화해 성능을 향상시킬 필요가 있다. 이를 위해 React.memo()에 대한 간단한 사용법을 기록해본다.\r\n\r\n# 1. React.memo()\r\n\r\n컴포넌트가 React.memo()로 래핑될 때, React는 컴포넌트를 렌더링하고 결과를 Memoizing한다.\r\n\r\n그리고 다음 렌더링이 일어날 때 props가 값다면, React는 Memoizing된 내용을 재사용한다.\r\n\r\n## 사용법\r\n\r\n```javascript\r\nimport React from \"react\";\r\n\r\nexport const MemoTest = ({ someProps }) => {\r\n console.log(\"Memo test, props 바뀔때만 콘솔 작동\");\r\n return (\r\n
\r\n

MemoTest 컴포넌트

\r\n

props가 바뀔때만 렌더링

\r\n count와 연동된 props: {someProps}\r\n
\r\n );\r\n};\r\n\r\n// 다른 컴포넌트에서 불러올 때, 해당 컴포넌트를 import 해주면 된다.\r\nexport const MemoTestComponent = React.memo(MemoTest);\r\n```\r\n\r\n## 사용 예시\r\n\r\n```javascript\r\nexport const TestApp = () => {\r\n const values = useContextValue();\r\n const update = useContextUpdate();\r\n const [count, setCount] = useState(0);\r\n console.log(\"최상위 컴포넌트 : 렌더링 될때마다 콘솔 작동\");\r\n\r\n const contextHandler = () => {\r\n values === \"someData\" ? update(\"dataSome\") : update(\"someData\");\r\n };\r\n\r\n return (\r\n
\r\n \r\n \r\n {\r\n setCount(pre => pre + 1);\r\n }}\r\n >\r\n +count버튼\r\n \r\n {\r\n setCount(pre => pre - 1);\r\n }}\r\n >\r\n -count버튼\r\n \r\n
\r\n

context value

\r\n value : {values}\r\n \r\n
\r\n
\r\n );\r\n};\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVQkSV%2Fbtr37vCBXzm%2FVXiqfYdD4H2FTcw3tGVFzk%2Fimg.png)\r\n\r\n위 이미지는 TestApp 코드의 초기 화면이다.\r\n\r\n모든 컴포넌트가 첫 렌더링을 거쳐 각각 콘솔을 찍어내고 있다.\r\n\r\n### TestApp의 count state는 MemoTest 컴포넌트에 props로 전달해주고 있기 때문에 React.memo()의 효과는 사용되지 않고, 일반 컴포넌트와 마찬가지로 TestApp count state가 변경될 때마다 렌더링되는 것을 볼 수 있다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/Hl6oT/btr4gSJ05tT/kipvw3VkehYfnxf2Pbq780/img.gif)\r\n\r\n### 하지만 contextAPI처럼 MemoTest의 props와 연관되지 않은 다른 방식을 통해 통해 리렌더링을 발생시키는 경우라면, memoTest 컴포넌트의 props가 변경되지 않기 때문에 MemoTest는 렌더링을 발생시키지 않는다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/cmaheq/btr36GRW8J1/uhzNP5d6ymqDxVDGFK4Ay1/img.gif)\r\n\r\n> contextAPI의 기본 사용법 : https://lee-yo-han.github.io/react/react-context-api\r\n\r\n---\r\n\r\n## 1-1 - props 동등 비교 커스터마이징\r\n\r\nReact.memo()는 props 혹은 props의 객체를 비교할 때 얕은 비교를 하기 때문에, 비교 방식을 수정하고 싶다면 React.memo() 두 번째 매개변수로 비교함수를 만들어 수동으로 연산 후 넘겨주면 된다.\r\n\r\n```javascript\r\nimport React from \"react\";\r\n\r\nexport const MemoTest = ({ someProps }) => {\r\n console.log(\"Memo test, props 바뀔때만 콘솔 작동\");\r\n return (\r\n
\r\n

MemoTest 컴포넌트

\r\n

props가 바뀔때만 렌더링

\r\n count와 연동된 props: {someProps}\r\n
\r\n );\r\n};\r\n\r\nconst propsAreEqual = (prev, next) => {\r\n return (\r\n prev.someProps === next.someProps\r\n // 다른 props들이 있는 경우 &&을 통해 연산 추가\r\n );\r\n};\r\n\r\nexport const MemoTestComponent = React.memo(MemoTest, propsAreEqual);\r\n```\r\n\r\n# 2. React.memo()는 언제 사용해야 하는가?\r\n\r\n### - 함수형 컴포넌트가 같은 props로 자주 렌더링 될거라 예상될 경우\r\n\r\nprops가 변경되지 않는 경우에도 상위 컴포넌트의 지속적인 렌더링에 의해 하위 컴포넌트의 불필요한 렌더링이 예상되는 경우라면 React.memo()를 통해 오직 props의 변화에만 반응시켜주는 것이 효율적이다.\r\n\r\n### - 무겁고 비용이 큰 연산이 있는 경우\r\n\r\n한 번 렌더링 될 때 큰 연산이 발생한다고 했을때 불필요한 렌더링이 일어난 경우 연산에 사용되는 비용이 낭비될 수 있기 때문에 이러한 경우 또한 React.memo()를 통한 memoization이 적절하다고 볼 수 있다.\r\n\r\n# 3. React.memo()를 언제 사용하지 말아야 하는가?\r\n\r\n### - 위에 언급한 상황과 맞지 않는 경우 React.memo()를 사용할 필요가 없을 가능성이 높다.\r\n\r\n경험적으로 ㅅ어능적인 이점을 얻지 못한다면 memoization을 사용하지 않는 것이 좋다. 성능 관련 변경이 잘못 적용된다면 성능이 오히려 악화될 수 있다.\r\n\r\n### - 기술적으로는 가능하지만 클래스 기반의 컴포넌트를 React.memo()로 래핑하는 것은 적절하지 않다.\r\n\r\n클래스 기반의 컴포넌트에서 memoization이 필요하다면, pureComponent를 확장하여 사용하거나, shouldComponentUpdate() 메서드를 구현하는 것이 적절하다.\r\n\r\n### - props가 자주 바뀌는 경우\r\n\r\nprops가 자주 바뀌는 경우도 React.memo()로 래핑하더라도 이전 props와 다음 props를 비교 함수를 통해 비교할때 거의 false를 반환할 것이기 때문에 props가 자주 바뀌는 경우도 React.memo()의 사용이 적절하지 않다.\r\n\r\n## 결론\r\n\r\n> React.memo는 memoization의 장점을 얻게 해주는 좋은 도구이지만, 렌더링 방지를 위해 memoization에만 의존하면 안된다.\r\n\r\n---\r\n\r\n## 참조\r\n\r\nhttps://dmitripavlutin.com/use-react-memo-wisely/\r\n\r\nhttps://ui.toast.com/weekly-pick/ko_20190731\r\n"},{"title":"react usecallback","description":"useCallback을 이용한 컴포넌트 최적화하기","category":"react","date":"2023-03-16","content":"\r\n# useCallback이란?\r\n\r\n> 특정 함수를 새로 만들지 않고 재사용하고 싶을때 사용하는 함수 메모이제이션용 React hook이다.\r\n\r\n```javascript\r\nconst memoFn = useCallback(() => {}, [dep]);\r\n```\r\n\r\n첫번째 인자의 함수를 두번째 인자의 종속성 배열 내의 값이 변경될 때까지 저장하여 재사용할 수 있도록 해준다.\r\n\r\njavascript에서 함수(function () {} or () => {})는 객체 리터럴( {...} )이 항상 새 객체를 생성하는 것과 유사하게 항상 다른 함수를 생성한다. 그렇다면 함수를 props 로 내려준다고 하면 하위 컴포넌트는 props가 변경되었다고 인식하게 된다.\r\n\r\n그렇게되면 React.memo를 이용한 최적화가 원하는대로 작동하지 않을 수 있기 때문에 이러한 점을 useCallback이 메꿔줄 수 있게 된다.\r\n\r\n> 종속성 배열이 없는 경우 매번 새 함수를 반환하기 때문에 항상 dependencises 배열을 포함해줘야 한다. (특정 상황에서 빈 배열 [] 은 있을 수 있지만, 배열 자체가 없으면 의미가 없어진다.)\r\n\r\n## 사용예\r\n\r\n```javascript\r\nconst add = useCallback(() => x + y, [x, y]);\r\n```\r\n\r\n# useCallback과 React.memo를 통한 최적화\r\n\r\n```javascript\r\n// 최상단 (1번) 컴포넌트\r\nimport { useState } from \"react\";\r\nimport { UseCallbackChildren } from \"./UseCallbackChildren\";\r\n\r\nexport const UseCallbackTest = () => {\r\n const [isDark, setIsDark] = useState(false);\r\n return (\r\n <>\r\n \r\n
\r\n \r\n \r\n );\r\n};\r\n\r\n// 중간 (2번) 컴포넌트\r\nimport React, { useCallback } from \"react\";\r\nimport { CallbackForm } from \"./UseCallbackForn\";\r\n\r\nexport const UseCallbackChildren = ({ referrer, productId, theme }) => {\r\n const post = (url, data) => {\r\n console.log(\"POST /\" + url);\r\n console.log(data);\r\n };\r\n\r\n const handleSubmit = useCallback(\r\n orderDetails => {\r\n post(\"/product/\" + productId + \"/buy\", {\r\n referrer,\r\n orderDetails,\r\n });\r\n },\r\n [productId, referrer],\r\n );\r\n\r\n return (\r\n \r\n \r\n \r\n );\r\n};\r\n\r\n// 하위 (3번) 컴포넌트\r\nimport React, { useState } from \"react\";\r\n\r\nexport const UseCallbackForm = ({ onSubmit }) => {\r\n const [count, setCount] = useState(1);\r\n\r\n console.log(\"강제로 느리게\");\r\n let startTime = performance.now();\r\n while (performance.now() - startTime < 500) {\r\n // Do nothing for 500 ms to emulate extremely slow code\r\n }\r\n\r\n const handleSubmit = e => {\r\n e.preventDefault();\r\n const formData = new FormData(e.target);\r\n const orderDetails = {\r\n ...Object.fromEntries(formData),\r\n count,\r\n };\r\n onSubmit(orderDetails);\r\n };\r\n\r\n return (\r\n \r\n

\r\n \r\n Note: ShippingForm is artificially slowed down!\r\n \r\n

\r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport const CallbackForm = React.memo(UseCallbackForm);\r\n```\r\n\r\n위 코드를 보면, 최상단(1)에 있는 다크모드는 중간(2) 컴포넌트까지만 전달해주고, 중간(2) 컴포넌트는 하위(3) 컴포넌트에 useCallback 함수만 전달해주고 있다.\r\n\r\nuseCallback은 함수를 캐시해 재사용할 수 있도록 도와주기 때문에 종속성이 변하지 않으면 계속 재사용할 수 있다. 즉, 동일한 props로써 함수를 하위(3) 컴포넌트에 내려주고 있기 때문에 하위 컴포넌트의 렌더링에 영향이 없다.\r\n\r\n이 상태로 코드를 작동시키면 하위(3) 컴포넌트에 있는 count를 동작할때마다 느려지겠지만, 다크모드와 submit 이벤트에는 영향을 주지 않는다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/bEYsKw/btr4gSLc8BG/hJKpnBKbFziku3fjI6hphK/img.gif)\r\n\r\n만약 하위(3) 컴포넌트의 React.memo()를 없애게 되면 submit 이벤트에는 영향이 없지만, 최상위(1) 컴포넌트의 다크모드 state 변경에 의해 하위(3) 컴포넌트의 렌더링이 일어나게 되어 다크모드와 count의 동작이 느려지는걸 볼 수 있다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/mfxA0/btr4iTijPZD/XNzKmFrScoUZ6qSyiCMV2K/img.gif)\r\n\r\n자세히 보면 콘솔이 먼저 찍히고 0.5초정도 뒤에 동작하는 것을 볼 수 있다.\r\n\r\n# useMemo와의 차이점\r\n\r\n> useMemo는 함수의 호출 결과를 캐시하고, useCallback은 함수 자체는 캐시한다. 그러므로 useCallback은 useMemo와 달리 제공한 함수를 호출하지 않는다. 대신 사용자가 제공한 함수를 캐시하기 때문에 불필요한 렌더링 없이 함수를 전달할 수 있다.\r\n\r\n## useMemo와 useCallback의 동시 사용\r\n\r\n```javascript\r\nfunction useCallback(fn, dependencies) {\r\n return useMemo(() => fn, dependencies);\r\n}\r\n```\r\n\r\n## useCallback이 유용한 경우\r\n\r\n- useCallback을 통해 props로 하위 컴포넌트에 넘겨줄때\r\n- useEffect 등 다른 hook의 종속성으로 이용할때\r\n- 종속성 변화가 적고 재사용이 용이할때\r\n\r\n## useCallback 남용 시 문제점\r\n\r\n> 모든 함수를 useCallback으로 감싸게 되면 컴포넌트가 리렌더 될 때마다 모든 함수가 다시 재생성될 필요가 있는지 검사하는 연산이 수행된다. 따라서 보통은 특정 함수가 props로 전달되어 불필요한 컴포넌트 리렌더를 유발할 때에만 적용하는 것이 좋다.\r\n\r\n### memo, useMemo, useCallback 등 어떤 Memoization이나 부적절하게 사용할 경우 오히려 성능이 떨어질 수 있기 때문에 상황에 맞게 적절하게 사용하는 것이 좋은 것 같다.\r\n\r\n---\r\n\r\n참조: https://beta.reactjs.org/reference/react/useCallback\r\n"},{"title":"react usememo","description":"useMemo를 이용한 컴포넌트 최적화하기","category":"react","date":"2023-03-16","content":"\r\n# useMemo란?\r\n\r\n> useMemo는 React.memo(), useCallback과 같이 컴포넌트를 최적화하기 위한 훅 중 하나이다.\r\n\r\n# useMemo를 사용하는 이유\r\n\r\nReact의 컴포넌트는 부모 컴포넌트가 렌더링되거나, 자신의 state변경, 상위 컴포넌트에서 내려받는 props가 변경될 때마다 다시 렌더링된다. 그런데 내려받는 props나 state가 여러 가지일때 한 개 props의 변경으로 모든 값이 다시 계산되는 경우 불필요한 비용이 누적되게 된다.\r\n\r\nuseMemo는 이러한 현상을 효율적으로 전환하기 위한 hook이다.\r\n\r\n## useMemo를 적용하기 전\r\n\r\n```javascript\r\n// 상위 컴포넌트\r\nimport { useState } from \"react\";\r\nimport { UseMemoChildren } from \"./UseMemoChildren\";\r\n\r\nexport const UseMemoTest = () => {\r\n const [number, setNumber] = useState(0);\r\n const [text, setText] = useState(\"\");\r\n\r\n const changeNumber = e => {\r\n setNumber(e.target.value);\r\n };\r\n const changeText = e => {\r\n setText(e.target.value);\r\n };\r\n\r\n return (\r\n
\r\n

UseMemoText

\r\n
\r\n

number

\r\n \r\n
\r\n
\r\n

text

\r\n \r\n
\r\n \r\n
\r\n );\r\n};\r\n\r\n// 하위 컴포넌트\r\nexport const UseMemoChildren = ({ number, text }) => {\r\n const getNumber = number => {\r\n console.log(\"숫자 변경\");\r\n return number;\r\n };\r\n\r\n const getText = text => {\r\n console.log(\"글자 변경\");\r\n return text;\r\n };\r\n\r\n const showNumber = getNumber(number);\r\n\r\n const showText = getText(text);\r\n\r\n return (\r\n
\r\n

Memo Child Component

\r\n

number: {showNumber}

\r\n

text: {showText}

\r\n
\r\n );\r\n};\r\n```\r\n\r\n상위 컴포넌트는 각 input 값이 변경될 때마다 그 값을 저장해 하위 컴포넌트로 내려준다.\r\n\r\n하위 컴포넌트는 내려받은 props를 통해 화면에 노출시켜줄 값을 return해주는 역할을 한다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/9hqzj/btr4hQeyTmU/PzYhcCiwuZq08zOkhBqeWk/img.gif)\r\n\r\n이 상태에서 input을 업데이트하면 숫자를 바꾸든 텍스트를 바꾸든 getNumber 함수와 getText 함수 둘 다 다시 연산되는 것을 볼 수 있다.\r\n\r\n이런 경우 변경된 props에 대해서만 다시 연산될 수 있도록 useMemo를 적용해볼 수 있다.\r\n\r\n## useMemo 사용법\r\n\r\n```javascript\r\nconst cachedValue = useMemo(calculateValue, dependencies);\r\n```\r\n\r\n### 적용 예시\r\n\r\n```javascript\r\n// 하위 컴포넌트\r\n\r\nimport { useMemo } from \"react\";\r\n\r\nexport const UseMemoChildren = ({ number, text }) => {\r\n // console.log(\"useMemoChildren start\");\r\n const getNumber = number => {\r\n console.log(\"숫자 변경\");\r\n return number;\r\n };\r\n\r\n const getText = text => {\r\n console.log(\"글자 변경\");\r\n return text;\r\n };\r\n\r\n const showNumber = useMemo(() => {\r\n return getNumber(number);\r\n }, [number]);\r\n\r\n const showText = useMemo(() => {\r\n return getText(text);\r\n }, [text]);\r\n\r\n return (\r\n
\r\n

Memo Child Component

\r\n

number: {showNumber}

\r\n

text: {showText}

\r\n
\r\n );\r\n};\r\n```\r\n\r\n위와 같이 useMemo를 적용하고 나면 다음과 같이 useMemo 적용 이후 변경된 props에 대한 연산만 처리해주는 것을 볼 수 있다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/4Vcyh/btr4hPz1xwm/0zNRNCdyJKwpsWk2LU73KK/img.gif)\r\n\r\n> useMemo 마지막의 dependencies [ ] 배열은 필수적으로 어떤 값을 변경에 반응할 것인지 포함시켜줘야 한다.\r\n\r\n# 연산 비용이 비싸다는 기준은?\r\n\r\nReact 공식 사이트에서 권장하는 useMemo 적용 기준은 연산에 소요된 시간이 1ms 이상일 경우로 안내하고 있다.\r\n\r\nconsole.time/timeEnd 등을 통해 useMemo를 적용하고자 하는 함수의 총 연산시간을 도출해볼 수 있다.\r\n\r\n```javascript\r\nconst getText = text => {\r\n console.time(\"memo\");\r\n console.log(\"글자 변경\");\r\n console.timeEnd(\"memo\");\r\n return text;\r\n};\r\n```\r\n\r\n위와 같이 코드 작성 후 text를 변경해보면 다음과 같은 콘솔을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8mGtS%2Fbtr4iU19jHE%2FH0huW2SytbAtalR3urepr0%2Fimg.png)\r\n\r\n> 입력하는 계산이 눈에 띄게 느리고, 종속성이 거의 변경되지 않는 경우에는 useMemo를 이용한 최적화가 적합하지만, 컴포넌트 구조 변경과 상태 관리의 최소화, state를 업데이트하는 Effect의 불필요한 종속성을 제거하거나 특정 함수들을 별도로 관리하는 방법이 useMemo보다 적합할 수 있다.\r\n\r\n> 실제로 React의 성능 문제 대부분은 Effect에서 발생하는 업데이트 체인으로 인해 발생한다고 한다.\r\n\r\n---\r\n\r\n참조 : https://beta.reactjs.org/reference/react/useMemo\r\n"},{"title":"react context api","description":"ContextAPI 사용하기","category":"react","date":"2023-03-15","content":"\r\n# ContextAPI\r\n\r\n> 간단한 데이터의 변화를 props drilling 없이 전역적으로 관리해주기 위해 ContextAPI를 사용해볼 수 있다.\r\n\r\n## 사용법\r\n\r\n## 1. context component 생성\r\n\r\n```javascript\r\n// context.jsx\r\nimport { createContext, useContext, useState } from \"react\";\r\n\r\n// context 생성\r\nconst AnyContextValue = createContext();\r\nconst AnyContextUpdate = createContext();\r\n\r\n// useContext 생성\r\nexport const useContextValue = () => {\r\n const context = useContext(AnyContextValue);\r\n return context;\r\n};\r\nexport const useContextUpdate = () => {\r\n const update = useContext(AnyContextUpdate);\r\n return update;\r\n};\r\n\r\n// context Component 생성\r\n// children props를 Contextname.provider로 감싸준다.\r\nexport const SomeContext = ({ children }) => {\r\n const [someData, setSomeData] = useState(\"someData\");\r\n return (\r\n \r\n \r\n {children}\r\n \r\n \r\n );\r\n};\r\n```\r\n\r\n## 2. context import\r\n\r\n```javascript\r\nimport { TestApp } from \"./components/blogtest/TestApp\";\r\nimport { SomeContext } from \"./components/context/RenderingContext\";\r\n\r\n// import된 Context를 적용하고자 하는 컴포넌트들의 최상위에 감싸준다.\r\nfunction App() {\r\n return (\r\n \r\n \r\n \r\n );\r\n}\r\n\r\nexport default App;\r\n```\r\n\r\n## 3. context 사용\r\n\r\n```javascript\r\nimport { useState } from \"react\";\r\n// 생성한 useContext import\r\nimport { useContextUpdate, useContextValue } from \"../context/RenderingContext\";\r\n\r\nexport const ChildrenComponent = () => {\r\n const values = useContextValue();\r\n const update = useContextUpdate();\r\n\r\n const contextHandler = () => {\r\n values === \"someData\" ? update(\"dataSome\") : update(\"someData\");\r\n };\r\n\r\n return (\r\n
\r\n

context value

\r\n value : {values}\r\n \r\n
\r\n );\r\n};\r\n```\r\n\r\n위와 같이 설정이 완료된 후 정상적으로 contextAPI를 사용할 수 있다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/d0hnZa/btr35w9FvGX/zDcTOjtXxg23vMKk6LxaM0/img.gif)\r\n\r\n> contextAPI는 간단하게 상태를 관리해줄 수 있지만, provider를 전역으로 감쌌을 때 컴포넌트들이 전반적으로 렌더링이 일어날 수 있기 때문에 상황과 구조에 맞게 적절하게 사용해주는 것이 좋다.\r\n\r\n---\r\n\r\n## 참고 : typescript에서의 contextAPI 사용\r\n\r\n### TypeScript에서는 context 초기값 등의 설정이 필요하다.\r\n\r\n```typescript\r\n// context component\r\n\r\nimport { createContext, useContext, useState } from \"react\";\r\nimport { PostType } from \"@/types/pages\";\r\nimport { ProviderProps, ContextType } from \".\";\r\n\r\n// context 초기화\r\nconst initialContext = {\r\n posts: [],\r\n setPosts: () => {},\r\n};\r\n\r\n// context 생성\r\nconst PostContextValue = createContext(initialContext.posts);\r\nconst PostContextUpdate = createContext(initialContext.setPosts);\r\n\r\n// useContext 생성\r\nexport const useMdContextValue = () => {\r\n const context = useContext(PostContextValue);\r\n return context;\r\n};\r\nexport const useMdContextUpdate = () => {\r\n const update = useContext(PostContextUpdate);\r\n return update;\r\n};\r\n\r\n// context component 생성\r\nexport const MdContext = ({ children }: ProviderProps) => {\r\n const [posts, setPosts] = useState([]);\r\n\r\n return (\r\n \r\n \r\n {children}\r\n \r\n \r\n );\r\n};\r\n\r\n// 다른 컴포넌트에서의 사용\r\nimport { useEffect } from \"react\";\r\nimport { useMdContextUpdate } from \"@/context/mdContext\";\r\n\r\nexport default function Home({ posts }: { posts: PostType[] }) {\r\n const update = useMdContextUpdate();\r\n\r\n useEffect(() => {\r\n if (posts) {\r\n update(posts);\r\n }\r\n }, [posts, update]);\r\n```\r\n"},{"title":"github git blog utterances comment","description":"utterances를 이용한 Nextjs Github 블로그 댓글 기능 구현","category":"github","date":"2023-03-14","content":"\r\n# utterances란?\r\n\r\n> Github repository의 이슈 기능을 활용해 내 웹사이트에 댓글 기능을 추가할 수 있는 도구로, 별도의 백엔드 구성을 하지 않고 댓글들을 관리할 수 있다는 장점이 있다.\r\n\r\n많이 비교되는 disqus라는 툴도 있다. 하지만, disqus는 댓글 작성을 누구나 할 수 있다는 장점이 있는 반면에 광고가 많다는 단점이 있다.\r\n그에 비해 utterances는 Github 계정 소유자만 댓글을 작성할 수 있지만, 별도의 광고도 없고 댓글 작성 시 Markdown을 지원한다는 특징이 있다.\r\n\r\n나는 utterances를 Github blog에 사용할 생각이기에 굳이 utterances의 장점을 버리고 disqus를 사용할 이유가 없었다.\r\n\r\n# 사용방법\r\n\r\n## 1. repository 생성\r\n\r\npublic으로 설정된 새 레포가 필요하다.\r\n\r\nGithub blog repo에 연동해도 상관은 없지만 댓글만을 위한 저장소를 만들어 놓는게 관리가 훨씬 쉬울 것 같아 새 repo를 생성해줬다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbq4F8z%2Fbtr34VuvScp%2FwmTQX2iIjKtMjlowowHw80%2Fimg.png)\r\n\r\n저장소 이름은 상관 없다.\r\n\r\n## 2. utterances 설치\r\n\r\nGithub app에서 설치할 수 있다.\r\n\r\nhttps://github.com/apps/utterances\r\n\r\n## 3. utterances access 설정\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPS87y%2Fbtr35bYlHCH%2FhE9akcKFM7qYE6yAfvKD4k%2Fimg.png)\r\n\r\napp이 설치되면 Configure를 통해 어떤 저장소에 연동해줄지 설정할 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F55bSC%2Fbtr37BBZd9Y%2FfSwpH2VCQKwXKpQkXAbO00%2Fimg.png)\r\n\r\n나는 선택한 저장소에만 연동되게 설정했다.\r\n\r\n## 4. utterances.json 생성\r\n\r\n```javascript\r\n{\r\n \"origins\": [\"https://{my url}\"]\r\n}\r\n```\r\n\r\n위와 같이 자신이 댓글 기능을 사용할 사이트를 utterances.json에 입력해준다.\r\n\r\n## 5. script를 블로그 repo code에 복사\r\n\r\nhttps://utteranc.es/?installation_id=35005881&setup_action=install\r\n\r\n해당 사이트에 들어가면 어떤 방식으로 script를 구성할지 설정할 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo9X4N%2Fbtr34VVyI7q%2F2PbuKqgkmKDtfogCNj7Nx0%2Fimg.png)\r\n\r\n> Issue mapping같은 경우, 댓글과 포스팅을 어떻게 연동할지 설정하는 것인데, 통상적으로 pathname이나 URL을 많이 사용하지만 본인 프로젝트의 상황에 맞춰 설정해주는 것이 가장 좋다.\r\n\r\n> 또한 한글 파일명을 사용해 URL이나 pathname을 생성하는 경우, 유니코드 인식 문제로 비정상적인 URL이 생성될 수 있기 때문에 설정에 유의해야 한다.\r\n\r\n```javascript\r\n// 복사할 스크립트\r\n\r\n```\r\n\r\n테마 색상도 다양하기 때문에 utterances 사이트에서 원하는 색상을 골라가며 설정해줘도 괜찮다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxYtiI%2Fbtr35FEVVmk%2FcS0dxRjlWfFukRPCaL9y61%2Fimg.png)\r\n\r\n위에 생성한 script는 통상적으로 댓글 부분에 해당되는 곳에 그대로 복사해 넣지만, 나는 Nextjs를 통해 댓글 컴포넌트를 생성할 것이기 때문에 다른 방식을 사용했다.\r\n\r\n```javascript\r\nexport const Comment = () => {\r\n return (\r\n {\r\n if (!elem) {\r\n return;\r\n }\r\n const scriptElem = document.createElement(\"script\");\r\n scriptElem.src = \"https://utteranc.es/client.js\";\r\n scriptElem.async = true;\r\n scriptElem.setAttribute(\"repo\", \"{utterances repo name}\");\r\n scriptElem.setAttribute(\"issue-term\", \"pathname\");\r\n scriptElem.setAttribute(\"theme\", \"github-dark\");\r\n scriptElem.setAttribute(\"label\", \"blog-comment\");\r\n scriptElem.crossOrigin = \"anonymous\";\r\n elem.appendChild(scriptElem);\r\n }}\r\n />\r\n );\r\n};\r\n```\r\n\r\n해당 컴포넌트에 사용하기 위해 기존의 script와 같은 속성들을 넣어줬다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fckai9y%2Fbtr4aKSLFRA%2F9p3HEGqDCEkfa8sg7Nnd0k%2Fimg.png)\r\n\r\n하지만 strict 모드를 false로 해도 종종 댓글찾이 2개가 되어버려 useEffect를 통해 1번만 script를 붙일 수 있도록 코드를 수정했다.\r\n\r\n```javascript\r\nimport { useEffect } from \"react\";\r\n\r\nexport const Comment = () => {\r\n const makeRef = (el: HTMLElement | null) => {\r\n if (!el) {\r\n return;\r\n } else {\r\n const scriptElem = document.createElement(\"script\");\r\n scriptElem.src = \"https://utteranc.es/client.js\";\r\n scriptElem.async = true;\r\n scriptElem.setAttribute(\"repo\", \"{utterances repo name}\");\r\n scriptElem.setAttribute(\"issue-term\", \"pathname\");\r\n scriptElem.setAttribute(\"theme\", \"github-dark\");\r\n scriptElem.setAttribute(\"label\", \"blog-comment\");\r\n scriptElem.crossOrigin = \"anonymous\";\r\n el.appendChild(scriptElem);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n const newEl = document.getElementById(\"comment-box\");\r\n makeRef(newEl);\r\n }, []);\r\n\r\n return
;\r\n};\r\n```\r\n\r\n코드 수정 후 원하는대로 동작하는 모습을 볼 수 있었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNZNaQ%2Fbtr38NhUPkk%2FSH2Dgj2Si71gH3Gykm9kYK%2Fimg.png)\r\n"},{"title":"github github api get repo data","description":"GithubAPI를 이용한 repository 내 Markdown 파일 정보 불러오기","category":"github","date":"2023-03-13","content":"\r\n# Github REST API 설명서\r\n\r\nhttps://docs.github.com/ko/rest?apiVersion=2022-11-28\r\n\r\n## GithubAPI 이용 시 토큰이 필요한 경우 아래와 같이 토큰 발급도 가능하다.\r\n\r\n- 우측 상단의 프로필을 통해 Settings 접근\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5d9NT%2Fbtr3XWt2ipS%2FlVrpZru5irR5mDft0cLaO0%2Fimg.png)\r\n\r\n- 좌측 하단의 Developer settings 접속\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnMYAN%2Fbtr37uQgb1j%2Fz3lG0JXTBq5gkIhvk1HEek%2Fimg.png)\r\n\r\n- Tokens나 Fine-grained tokens를 통해 원하는 권한들을 설정해 토큰을 생성할 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKOqYU%2Fbtr3V6YgATa%2Fc3QmI12TDR3vgoNL0ZTZR1%2Fimg.png)\r\n\r\n> Github 홈페이지에 여러 api들이 많기 때문에 필요에 따라 사용하면 좋을 것 같다. 이 글에서는 repository 안의 파일들의 정보만을 가져올 것이다.\r\n\r\n## 1. requestURL\r\n\r\n```javascript\r\nconst BASE_URL =\r\n \"https://api.github.com/repos/{유저명을 포함한/레포지토리 이름}/contents/{데이터를 가져올 디렉토리}\";\r\nconst options = {\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n },\r\n};\r\n```\r\n\r\n예를 들어 src의 posting이라는 디렉토리의 데이터들을 가져오고 싶다면\r\n\r\nhttps://api.github.com/repos/name/some-repo/contents/src/posting\r\n\r\n정도가 될 수 있다.\r\n\r\n요청이 정상적으로 작동했다면 아래와 같이 데이터를 받을 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXHDFb%2Fbtr3WcDXFiZ%2FmFW3Q3uyQPHEOBpkOFVKA0%2Fimg.png)\r\n\r\n하지만 하나의 파일에 대한 구체적인 정보는 들어있지 않다.\r\n\r\n한 단계로 더 들어가 정보를 받아온다면 아래와 같은 base64로 인코딩된 content 데이터를 받아올 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaVD0e%2Fbtr38M4cfZ6%2FMLUYZ4OarG3pXvjnSCW7VK%2Fimg.png)\r\n\r\n## 2. decoding\r\n\r\n해당 콘텐츠의 데이터를 정상적으로 받아오기 위해서는 디코딩이 필요하다.\r\n\r\n```typescript\r\nconst eachMarkdown = async (name: string) => {\r\n const url = `${BASE_URL}/${categoryName}/${name}`;\r\n const response = await axios.get(url, options);\r\n const content = Buffer.from(response.data.content, \"base64\").toString();\r\n\r\n ...\r\n};\r\n```\r\n\r\ncontent 데이터를 추출하고 콘솔을 확인하면 아래와 같이 string을 받아볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flctvp%2Fbtr38LKZ6o1%2FnwU9EK44kJmKCfanQsdw0K%2Fimg.png)\r\n\r\n## 3. 데이터 가공\r\n\r\n캡쳐 이미지 아래로 마크다운 파일의 본문들이 함께 있지만, 내가 필요한 부분은 머리말 부분이기 때문에 이 데이터를 추출해야 했다.\r\n\r\n```typescript\r\nconst eachMarkdown = async (name: string) => {\r\n const url = `${BASE_URL}/${categoryName}/${name}`;\r\n const response = await axios.get(url, options);\r\n const content = Buffer.from(response.data.content, \"base64\").toString();\r\n const frontmatter = content.match(/^---\\n([\\s\\S]+?)\\n---/);\r\n if (frontmatter) {\r\n const matters = JSON.stringify(frontmatter[0]).split(\"\\\\n\");\r\n const dateRegex = /\\d{4}-\\d{2}-\\d{2}/;\r\n const match = matters[4].match(dateRegex);\r\n if (match) {\r\n const result = {\r\n title: matters[1].split(\"title: \")[1].replaceAll(\" \", \"-\"),\r\n description: matters[2].split(\"description: \")[1],\r\n category: matters[3].split(\"category: \")[1],\r\n date: match[0],\r\n };\r\n }\r\n }\r\n};\r\n```\r\n\r\n상수 frontmatter는 머리말을 가져오지만 예쁘게 가져오진 않는다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTC1yi%2Fbtr35bRukP2%2FnWJ0mgeFgu2LlEBk0KyupK%2Fimg.png)\r\n\r\n때문에 if(frontmatter){...}와 같이 필요한 데이터를 정규식이나 string메서드를 통해 원하는대로 가공해 사용하여 사용해야 한다.\r\n"},{"title":"github git use markdown viewer","description":"Github pages 블로그 Markdown 불러오기","category":"github","date":"2023-03-12","content":"\r\n## Markdown을 통해 블로그를 작성하는 이유\r\n\r\n- 머리말을 따로 설정해 route 링크를 설정해주거나 제목, 날짜, 카테고리, 등을 설정하여 블로그 탐색을 용이하게 해줄 수 있다.\r\n- Markdown을 repository에 저장함으로써 githubAPI 등을 이용한 데이터 조회를 가능하게 한다.\r\n\r\n## 필요한 패키지\r\n\r\n```bash\r\nyarn add gray-matter # 문자열이나 파일에서 머리말을 구문 분석, 파일에서 메타데이터와 내용 등 추출 시 사용\r\nyarn add marked\r\nyarn add @types/marked # 타입스크립트의 경우\r\nyarn add react-markdown #
과 유사함\r\nyarn add remark-gfm # 마크다운 문법이 다양하게 적용될 수 있도록 도와주는 플러그인\r\nyarn add react-syntax-highlighter\r\nyarn add @types/react-syntax-highlighter # 타입스크립트의 경우\r\n```\r\n\r\n참조\r\n\r\nhttps://yarnpkg.com/package/react-markdown\r\n\r\nhttps://yarnpkg.com/package/react-syntax-highlighter\r\n\r\n## posting 파일들은 src 파일 안에 있다고 가정\r\n\r\n## 1. [slug].tsx 파일 생성\r\n\r\n```javascript\r\n\r\nimport { GetStaticPaths, GetStaticProps } from \"next\";\r\nimport { join } from \"path\";\r\nimport fs from \"fs/promises\";\r\nimport matter from \"gray-matter\";\r\nimport ReactMarkdown from \"react-markdown\";\r\nimport remarkGfm from \"remark-gfm\"; // Link, table, checklist 등의 형식을 표현할 수 있게 해줌\r\nimport { Prism as SyntaxHighlighter } from \"react-syntax-highlighter\";\r\n// import { darcula } from \"react-syntax-highlighter/dist/esm/styles/prism\"; - 에러 발생\r\nimport { dark } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\nimport { vsDark } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\nimport { darcula } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\n\r\n\r\ninterface Props {\r\n title: string;\r\n date: string;\r\n content: string;\r\n}\r\n\r\nexport default function BlogPost({ title, date, content }: Props) {\r\n return (\r\n
\r\n

{title}

\r\n

{date}

\r\n \r\n {String(children).replace(/\\n$/, \"\")}\r\n \r\n ) : (\r\n \r\n {children}\r\n \r\n );\r\n },\r\n }}\r\n >\r\n {content}\r\n \r\n
\r\n );\r\n}\r\n\r\n// 파일명을 통해 url 생성\r\nexport const getStaticPaths: GetStaticPaths = async () => {\r\n const postsDirectory = join(process.cwd() + \"src\" + \"/posting\", \"blog\");\r\n const filenames = await fs.readdir(postsDirectory);\r\n const paths = filenames.map(filename => ({\r\n params: { slug: filename.replace(/\\.md$/, \"\") },\r\n }));\r\n return { paths, fallback: false };\r\n};\r\n\r\n// url에 해당하는 파일명을 찾아 matter로 데이터 추출\r\nexport const getStaticProps: GetStaticProps = async ({ params }) => {\r\n const slug = params?.slug as string;\r\n const filePath = join(process.cwd() + \"src\" + \"/posting\", \"blog\", `${slug}.md`);\r\n const fileContents = await fs.readFile(filePath, \"utf8\");\r\n const { data, content } = matter(fileContents);\r\n return {\r\n props: {\r\n title: data.title,\r\n date: data.date,\r\n content,\r\n },\r\n };\r\n};\r\n\r\n```\r\n\r\n## 2. SyntaxHighlighter style 설정\r\n\r\nSyntaxHighlighter 내부의 코드블록 style을 이용하기 위해서는 그에 맞는 소스를 가져와야 한다.\r\n\r\n### 에러가 발생하는 ...dist/esm/styles/prism\r\n\r\nimport { darcula } from \"react-syntax-highlighter/dist/esm/styles/prism\";\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrNEOn%2Fbtr34VAZ3qk%2Fhy9KPHftPMrHNswKxBdBk0%2Fimg.png)\r\n\r\n### 정상 작동하는 imports ...dist/cjs/styles/prism\r\n\r\n```javascript\r\nimport { darcula } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQTv0s%2Fbtr37t4PDmS%2Fc0Sw6mnao0GUU05pG0mnb0%2Fimg.png)\r\n\r\n```javascript\r\nimport { dark } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdjz47g%2Fbtr35dhlOF6%2FmmOkOQB6R1x5TgzkEcQ27K%2Fimg.png)\r\n\r\n```javascript\r\nimport { vsDark } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F29uf2%2Fbtr3V6KDHYg%2FeTnSoSaIAgZYqOcK7MTr8K%2Fimg.png)\r\n\r\n원하는 스타일을 SyntaxHighlighter style에 넣어주면 된다.\r\n\r\n> Markdown content(내용)는 ReactMarkdown 컴포넌트의 className을 통해 수정해줄 수 있다.\r\n\r\n## 3. Markdown 파일 생성\r\n\r\n```markdown\r\n// 파일명 : first-post.md\r\n--- 머리말\r\ntitle: First-Post\r\ndate: \"2023-02-28\" <- 숫자만 사용하면 newDate()처럼 생성되어 타입 오류 발생 가능\r\n\r\n---\r\n\r\n# Deploy and test\r\n\r\ntest\r\n```\r\n\r\nMarkdown 파일의 데이터를 불러올때, 머리말을 이용한 metaTag 설정이나 제목 등을 자유롭게 설정할 수 있다.\r\n\r\n파일 생성 후 해당 링크 접속 시 정상적으로 접속이 가능해진다.\r\n\r\n## ex\r\n\r\nlocalhost:3000/first-post\r\n\r\nurl은 [slug].tsx 파일이 위치한 경로에 따라 달라질 수 있다. (nextjs 라우팅 방식)\r\n"},{"title":"github git custom blog","description":"Nextjs를 이용한 Github pages 블로그 만들기","category":"github","date":"2023-03-11","content":"\r\n# Github pages와 Nextjs를 선택한 이유\r\n\r\n### 1. Github pages\r\n\r\n> vercel도 Nextjs로 쉽게 배포할 수 있지만, 백엔드는 별도로 만들지 않을 계획이었기에, 추후 댓글 기능이나 추천 글 목록 등을 Github 사이트 하나로 모두 관리하기 위해서 Github pages를 이용하기로 했다.\r\n\r\n### 2. Nextjs\r\n\r\n> 블로그는 검색엔진에 잘 노출되어야 그 의미가 있다. 또한 React 환경을 이용한 블로그 개발을 원했기에 SSR(Server Side Rendering) 등을 통한 SEO(검색엔진 최적화)에 유리한 React 프레임워크인 Nextjs를 사용하기로 결정했다.\r\n\r\n# Github Pages 호스팅 방법\r\n\r\n### 1. Repository 생성\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkBnCf%2Fbtr3TGZh4t7%2F0VD4ycS4u8EvTAeIxvjsP1%2Fimg.png)\r\n\r\nRepo 이름은 {username}.github.io 로 저장해준다.\r\n\r\n- 생성된 repo의 settings => pages에서 배포를 확인할 수 있다. (배포되는데 5~10분 정도 소요될 수 있다.)\r\n\r\n### 2. Repository 클론 후 Nextjs 설치\r\n\r\n```bash\r\n# 현재 디렉토리에 설치\r\nyarn create next-app .\r\n\r\n# typescript\r\nyarn create next-app . --typescript\r\n```\r\n\r\n### 3. gh-pages 설치\r\n\r\nGithub pages를 이용하기 위해 아래 명령어를 통해 설치해준다.\r\n\r\n```bash\r\nyarn add gh-pages --save-dev\r\n```\r\n\r\n### 4. package.json 설정\r\n\r\n홈페이지 도메인 추가 및 scripts를 수정해준다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSbOFc%2Fbtr33yMA8BY%2FYBMjkb8aNMMR8jv7bhlrxk%2Fimg.png)\r\n\r\n1번째 줄처럼 홈페이지를 입력하고, scripts를 위와 같이 수정해준다.\r\n\r\n```javascript\r\n\"build\": \"next build && next export\",\r\n\"predeploy\": \"npm run build\",\r\n\"deploy\": \"touch out/.nojekyll && gh-pages -d out --dotfiles\"\r\n```\r\n\r\n- next export : Nextjs를 정적 사이트로 배포하기 위해 설정\r\n- -- touch out/.nojekyll : 본래 guthub pages는 jekyll 기반으로, \\_\\_{filename}을 특수 리소스로 간주하고 최종 사이트에 복사하지 않기 때문에 jekyll을 사용하지 않는다고 명시해 정상적으로 처리될 수 있도록 설정\r\n- gh-pages -d out --dotfiles : 현재 repo의 임시 복제본 생성 후, gh-pages 브랜치가 없는 경우 브랜치 생성. 기본 경로의 모든 파일 또는 선택적 src 구성의 패턴과 일치하는 파일만 복사하고 모든 변경 사항 커밋 후 푸시. 이미 gh-pages 브랜치가 있는 경우 제공된 파일에서 커밋을 추가하기 전에 원격의 모든 커밋으로 업데이트됨. src 패턴과 일치하는 도트 파일도 포함시킴\r\n\r\n참조 : https://github.com/tschaub/gh-pages\r\n\r\n명령어 보기\r\n\r\n```bash\r\nyarn gh-pages --help\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJQQKR%2Fbtr34cpsLGR%2FDtpXfGfLf2p8bph05dvvnk%2Fimg.png)\r\n\r\n### next.config.js 수정\r\n\r\nconfig는 본인이 적용하려는 방식에 맞게 설정해줘도 괜찮다.\r\n\r\n```javascript\r\n/** @type {import('next').NextConfig} */\r\nconst nextConfig = {\r\n reactStrictMode: false,\r\n images: {\r\n unoptimized: true, // 이미지 정상적으로 불러올 수 있도록함\r\n },\r\n compiler: {\r\n styledComponents: true, // styled-components 사용 시 컴파일러에 추가\r\n },\r\n};\r\n\r\nmodule.exports = nextConfig;\r\n```\r\n\r\n### 6. 배포\r\n\r\n```bash\r\nyarn deploy\r\n```\r\n\r\n> cmd(명령 프롬프트)에서 실행 시 tought를 사용할 수 없다는 에러가 발생하기 때문에 git bash로 실행\r\n\r\n### 7. 배포 후 설정\r\n\r\n```bash\r\nExport successful. Files written to D:\\0. CORDING\\INDI-PROJECT\\github-blog\\out\r\n$ gh-pages -d out --dotfiles\r\nPublished\r\n```\r\n\r\n터미널에 위와 같은 메세지가 뜨면, repository => setting => pages의 branch에 gh-pages가 생성돼있는 것을 볼 수 있음\r\n\r\ngh-pages를 배포 브랜치로 설정해준 후 저장\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXcGvE%2Fbtr35F5rS1r%2FgTSz4ck3EW2y6KOnmS9EiK%2Fimg.png)\r\n\r\n설정을 완료하고 상단의 Visit site로 페이지를 접속하면 Nextjs 기본 화면이 잘 나오는 것을 볼 수 있다.\r\n"},{"title":"css a tag style fix","description":"a 태그(Link 태그) 기본 스타일 제거하기","category":"css","date":"2023-03-09","content":"\r\n# a태그 기본 스타일 제거와 스타일 적용\r\n\r\n```css\r\n/* 기본 밑줄 제거 */\r\na {\r\n text-decoration: none;\r\n}\r\n\r\n/* 아직 방문하지 않은 링크의 글자색 정의 */\r\na:link {\r\n color: pink;\r\n}\r\n\r\n/* 사용자가 방문한 적이 있는(클릭한 이후) 링크의 글자색 \r\n정의 */\r\na:visited {\r\n color: black;\r\n}\r\n\r\n/* 마우스를 링크에 올려두었을 때, 글자색을 정의 */\r\na:hover {\r\n color: red;\r\n}\r\n\r\n/* 마우스로 링크를 클릭하고 뗄 때까지의 글자색을 정의 */\r\na:active {\r\n color: green;\r\n}\r\n```\r\n"},{"title":"css no selection","description":"CSS로 글자 더블클릭 시 선택자(파란색 마스킹) 없애기","category":"css","date":"2023-03-09","content":"\r\n# 더블클릭시 파란색 선택자 생성 막는 방법\r\n\r\n```css\r\ndiv {\r\n user-select: none;\r\n -webkit-user-select: none;\r\n /* (safari, chrome) browsers */\r\n -moz-user-select: none;\r\n /* mozilla browsers */\r\n -khtml-user-select: none;\r\n /* konqueror browsers */\r\n\r\n -ms-user-select: none; /* IE10+ */\r\n}\r\n```\r\n"},{"title":"github git cmd","description":"깃 명령어 모음","category":"github","date":"2023-03-09","content":"\r\n# 깃 명령어 모음\r\n\r\n## 디렉토리\r\n\r\n1. 홈 디렉토리로 이동\r\n\r\n```bash\r\n$ cd ~\r\n```\r\n\r\n2. 새 디렉토리에 디렉토리명을 생성\r\n\r\n```bash\r\n$ mkdir [디렉토리명]\r\n```\r\n\r\n3. [디렉토리명]으로 이동\r\n\r\n```bash\r\n$ cd [디렉토리명]\r\n```\r\n\r\n4. 부모 디렉토리로 이동\r\n\r\n```bash\r\n$ cd ..\r\n```\r\n\r\n5. 현재 경로를 출력 // print working directory\r\n\r\n```bash\r\n$ pwd\r\n```\r\n\r\n6. 디렉토리의 내용을 출력\r\n\r\n```bash\r\n$ ls\r\n```\r\n\r\n7. 디렉토리의 폴더 상세 정보까지 출력\r\n\r\n```bash\r\n$ ls -l\r\n```\r\n\r\n8. 디렉토리의 숨김 파일과 디렉토리까지 출력\r\n\r\n```bash\r\n$ ls -a\r\n```\r\n\r\ntip: 옵션은 합쳐서 ls -al 처럼 사용이 가능하다.\r\n\r\n9. 파일이 있는 디렉토리를 내용물과 함께 삭제\r\n\r\n```bash\r\n$ rm -r [디렉토리명]\r\n```\r\n\r\n10. vi 편집기로 [파일명.확장자명] 파일을 작성\r\n\r\n```bash\r\n$ vim [파일명.확장자명]\r\n```\r\n\r\n11. 터미널 창의 내용을 삭제\r\n\r\n```bash\r\n$ clear\r\n```\r\n\r\n12. 터미널 창을 종료\r\n\r\n```bash\r\n$ exit\r\n```\r\n\r\n## git 유저 / 업로드\r\n\r\n1. 현재 위치에서 지역 저장소를 생성\r\n\r\n```bash\r\n$ git init\r\n```\r\n\r\n2. 깃 환경에서 사용자 이름을 [사용자명]으로 지정\r\n\r\n```bash\r\n$ git config --global user.name \"[사용자명]\"\r\n```\r\n\r\n3. 깃 환경에서 사용자 이메일을 [사용자이메일명]으로 지정\r\n\r\n```bash\r\n$ git config --global user.email \"[사용자이메일명]\"\r\n```\r\n\r\n4. 깃의 상태를 확인\r\n\r\n```bash\r\n$ git status\r\n```\r\n\r\n## commit 명령어\r\n\r\n1. [파일명.확장자명]을 스테이지에 올림\r\n\r\n```bash\r\n$ git add [파일명.확장자명]\r\n```\r\n\r\n2. 커밋 메시지 [메시지명]을 붙여 커밋\r\n\r\n```bash\r\n$ git commit -m \"[메시지명]\"\r\n```\r\n\r\n3. 메시지 [메시지명]을 붙여서 스테이징과 커밋을 동시에 진행\r\n\r\n```bash\r\n$ git commit -a -m \"[메시지명]\"\r\n```\r\n\r\n4. 커밋 내역 확인\r\n\r\n```bash\r\n$ git log\r\n$ git log --pretty=oneline # 한줄로 표기하기\r\n```\r\n\r\n5. 특정 커밋 내역 확인\r\n\r\n```bash\r\n$ git show [커밋 id]\r\n```\r\n\r\n6. 최근 버전과 작업 폴더의 수정 파일 사이의 차이를 출력\r\n\r\n```bash\r\n$ git diff\r\n$ git diff [이전커밋 id] [이후커밋 id]\r\n```\r\n\r\n7. 지정한 커밋 해시로 이동\r\n\r\n```bash\r\n$ git checkout [커밋 해시]\r\n```\r\n\r\n8. 가장 최근 커밋을 취소\r\n\r\n```bash\r\n$ git reset HEAD^ # 현재 HEAD의 이전 커밋으로 되돌리기\r\n$ git reset HEAD~n # 현재로 부터 n 번째 이전 커밋으로 되돌리기\r\n```\r\n\r\n9. 지정한 커밋 해시로 이동하고 커밋을 취소\r\n\r\n```bash\r\n$ git reset [커밋 해시]\r\n\r\n# reset의 3가지 옵션\r\n$ git reset --soft [커밋ID] # head 만 바뀜\r\n$ git reset --mixed [커밋ID] # staging 도 그 때로 바뀜\r\n$ git reset --hard [커밋ID] # working디렉토리/staging 모두 그 때로 바꿈\r\n```\r\n\r\n10. 지정한 커밋 해시의 변경 이력을 취소\r\n\r\n```bash\r\n$ git revert [커밋 해시]\r\n```\r\n\r\n11. 커밋 수정하는 법\r\n\r\n```bash\r\n# 파일 수정 한 뒤\r\n$ git add .\r\n$ git commit --amend : 최신 커밋 수정\r\n```\r\n\r\n## 브랜치 명령어\r\n\r\n1. 새로운 브랜치 [브랜치명]을 생성\r\n\r\n```bash\r\n$ git branch [브랜치명]\r\n# 브랜치 조회하기\r\n$ git branch\r\n```\r\n\r\n2. [브랜치명]으로 체크아웃(이동)\r\n\r\n```bash\r\n$ git checkout [브랜치명]\r\n$ git checkout -b [브랜치명] # 브랜치만들고 바로 이동\r\n# 브랜치 삭제\r\n$ git branch -d 브랜치명\r\n```\r\n\r\n3. 커밋 로그에서 한 줄에 한 커밋씩 출력\r\n\r\n```bash\r\n$ git log --oneline\r\n```\r\n\r\n4. 수정한 전체 파일을 스페이지에 올린다.\r\n\r\n```bash\r\n$ git add .\r\n```\r\n\r\n5. 커밋 로그에 각 브랜치의 커밋을 그래프로 표시\r\n\r\n```bash\r\n$ git log --branches --graph\r\n```\r\n\r\n6. [브랜치명]을 master 브랜치와 병합\r\n\r\n```bash\r\n$ git merge [브랜치명]\r\n$ git merge [브랜치명] --edit # 병합 후 바로 vi 편집기가 나오면서 커밋 메시지 수정 가능\r\n$ git merge [브랜치명] --no-edit # 커밋 메시지 수정없이 바로 병합\r\n```\r\n\r\n7. merge 취소하기\r\n\r\n```bash\r\n$ git merge --abort\r\n```\r\n\r\n## git hub 원격 저장소\r\n\r\n1. 원격 저장소에 연결\r\n\r\n```bash\r\n$ git remote add origin [github 레포지 주소]\r\n$ git remote add origin [branch 이름] #없으면 생성됨\r\n```\r\n\r\n2. 원격 저장소에 연결됐는지 확인\r\n\r\n```bash\r\n$ git remote -v\r\n```\r\n\r\n3. 지역 저장소의 커밋을 맨 처음 원격 저장소에 올리는 경우\r\n\r\n```bash\r\n$ git push -u origin master\r\n```\r\n\r\n4. 3번을 한 후에 지역 저장소의 커밋을 원격 저장소에 올리는 경우(업로드)\r\n\r\n```bash\r\n$ git push\r\n$ git push origin master\r\n```\r\n\r\n5. 원격 저장소의 커밋을 지역 저장소로 가져옴\r\n\r\n```bash\r\n$ git pull\r\n$ git pull origin master\r\n```\r\n\r\n6. SSH 키를 생성함\r\n\r\n```bash\r\n$ ssh-keygen\r\n```\r\n\r\n7. 원격 저장소 복제하기\r\n\r\n```bash\r\n# 첫번째 커밋이 아니라면 풀 먼저하기\r\n$ git remote remove origin\r\n\r\n#원격 저장소를 [지역저장소명]에 복제하기\r\n$ git clone [원격 저장소 주소]\r\n```\r\n\r\n8. 원격 저장소의 커밋을 가져오기만 하고 merge하지 않는다\r\n 가져온 branch 내용은 origin/[브랜치] 로 저장됨\r\n\r\n```bash\r\n$ git fetch\r\n# 이후엔 diff 로 비교\r\n$ git diff test origin/test # 브랜치 이름이 test일 경우 예시\r\n```\r\n\r\n9. 패치로 가져온 정보가 있는 브랜치\\[FETCH_HEAD\\]로 이동\r\n\r\n```bash\r\n$ git checkout FETCH_HEAD\r\n```\r\n\r\n10. 패치로 가져온 정보가 있는 브랜치[FETCH_HEAD]를 master 브랜치에 병합한다\r\n\r\n```bash\r\n$ git merge FETCH_HEAD\r\n```\r\n\r\n11. [브랜치명]을 만드는 것과 동시에 체크아웃한다\r\n\r\n```bash\r\n$ git checkout -b [브랜치명]\r\n```\r\n\r\n12. 원격 저장소에 [브랜치명]의 브랜치의 커밋을 올린다\r\n\r\n```bash\r\n$ git push origin [브랜치명]\r\n```\r\n\r\n13. 원격저장소 삭제\r\n\r\n```bash\r\n$ git remote remove origin\r\n```\r\n\r\n## 파일/보관 명령어(stash)\r\n\r\n1. 파일 내용 출력\r\n\r\n```bash\r\n$ cat [파일명.확장자명]\r\n```\r\n\r\n2. 디렉토리를 만드는 동시에 지역저장소 생성\r\n\r\n```bash\r\n$ cd init [디렉토리명]\r\n```\r\n\r\n3. 현재 커밋을 다른 브랜치에 있는 [커밋메시지] 커밋으로 되돌림\r\n\r\n```bash\r\n$ git reset [커밋메시지] [커밋해시]\r\n```\r\n\r\n4. 병합이 끝난 [브랜치명]을 삭제\r\n\r\n```bash\r\n$ git branch [브랜치명] -d\r\n```\r\n\r\n5. 작업 트리의 수정 내용 stash에 따로 보관하기\r\n\r\n```bash\r\n$ git stash\r\n$ git stash save\r\n```\r\n\r\n6. 보관한 내용을 목록을 출력\r\n\r\n```bash\r\n$ git stash list\r\n```\r\n\r\n7. 보관한 내용을 적용\r\n\r\n```bash\r\n$ git stash apply\r\n$ git stash apply stash@{1}\r\n```\r\n\r\n8. 보관한 내용 중 가장 최근 항목을 삭제\r\n\r\n```bash\r\n$ git stash drop\r\n$ git stash drop stash@{1}\r\n```\r\n\r\n9. stash를 apply하고 제거(drop) 하기\r\n\r\n```bash\r\n$ git stash pop\r\n```\r\n\r\n## 기타 명령어\r\n\r\n1. 긴 명령어 짧게 이름 붙여 사용하기\r\n\r\n```bash\r\n# ex) git log --pretty=oneline을\r\n# ->git history 라는 별명으로 바꾸기.\r\n\r\ngit config alias.[별명] '원하는 명령어'\r\ngit config alias.history 'log --pretty=oneline'\r\n```\r\n\r\n2. tag 설정 하기\r\n\r\n```bash\r\n$ git tag [태그이름][커밋 ID]\r\n$ git tag Version_2 86a99 # tag 달기\r\n$ git tag #tag 조회하기\r\n$ git show Version_2\r\n```\r\n"},{"title":"html meta tag","description":"SEO(검색엔진 최적화)를 위한 HTML의 meta tag 간단 정리","category":"html","date":"2023-03-09","content":"\r\n# 메타태그\r\n\r\n### 메타태그란 쉽게 말해서 눈에 보이지 않는 문서가 가지고 있는 정보들을 담을 수 있는 태그를 말한다.\r\n\r\n## 1. 주로 사용해볼 수 있는 요소\r\n\r\n검색 엔진에 검색되는 단어 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n홈페이지 주제 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n제목 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n검색 결과에 표시되는 문자 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n검색 로봇 제어\r\n\r\n```html\r\n\r\n```\r\n\r\n날짜(제작일)\r\n\r\n```html\r\n\r\n```\r\n\r\n제작자명 지정\r\n\r\n```html\r\n\r\n```\r\n\r\nog: 태그\r\n\r\n```html\r\n\r\n\r\n\r\n\r\n```\r\n\r\n## 2. 사용 빈도 낮아보이는 요소\r\n\r\n제작사 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n웹 책임자 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n제작 도구 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n메일 주소 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n```html\r\n\r\n```\r\n\r\n파일 이름 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n위치 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n배포자 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n저작권 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n제작 년/월/일 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n최종 수정일 정의\r\n\r\n```html\r\n\r\n```\r\n\r\n## 3. 사용 빈도가 많이 낮아보이는 요소\r\n\r\n그림 위에 마우스 오버시 이미지 관련 툴바 생성하지 않음\r\n\r\n```html\r\n\r\n```\r\n\r\n캐시 금지\r\n\r\n```html\r\n\r\n```\r\n\r\n```html\r\n\r\n```\r\n\r\n캐시 만료일 정의\r\n\r\n```html\r\n\r\n```\r\n\r\n60초마다 새로고침\r\n\r\n```html\r\n\r\n```\r\n\r\n입력한 주소로 5초후 이동하는 것을 정의\r\n\r\n```html\r\n\r\n```\r\n\r\n브라우저 호환성 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n웹 페이지에 쓰인 언어 지정\r\n\r\n````html\r\n\r\n```html\r\n\r\n````\r\n\r\n## 4. 써볼만한 태그\r\n\r\n페이지 인입 시 장면 전환 효과\r\n\r\n```html\r\n\r\n```\r\n\r\n- Box out : 네모난 박스가 안쪽에서 바깥쪽으로\r\n- Circle in : 원이 바깥에서 안쪽으로\r\n- Circle out : 원이 안쪽에서 바깥쪽으로\r\n- Wipe up : 이미지의 아래에서 위쪽으로 수직 이동\r\n- Wipe down : 이미지의 위에서 아래쪽으로 수직 이동\r\n- Wipe right : 이미지의 왼쪽에서 오른쪽으로 수평 이동\r\n- Wipe left : 이미지의 오른쪽에서 왼쪽으로 수평 이동\r\n- Vertical blinds : 수직 블라인드가 쳐지는 형태로 변환\r\n- Horizontal blinds : 수평 블라인드가 쳐지는 형태로 변환\r\n- Checkerboard across : 바둑판 형태의 격자가 왼쪽에서 오른쪽으로 - 생성\r\n- Checkerboard down : 바둑판 형태의 격자가 위에서 아래로 생성\r\n- Random dissove : 안개와 비슷한 형태로 전환\r\n- Split vertical in : 왼쪽과 오른쪽 끝에서 중앙으로 수직 이동\r\n- Split vertical out : 중앙에서 양쪽 끝으로 수직 이동\r\n- Split Horizontal in : 양쪽에서 중앙으로 수평 이동\r\n- Split Horizontal out : 중앙에서 양쪽끝으로 수직이동\r\n- Strips left down : 대각선 형태로 오른쪽 상단에서 왼쪽 하단으로 - 이동\r\n- Strips left up : 대각선 형태로 오른쪽 하단에서 왼쪽 상단으로 이동\r\n- Strips right down : 대각선 형태로 왼쪽 상단에서 오른쪽 하단으로 - 이동\r\n- Strips right up : 대각선 형태로 왼쪽 하단에서 오른쪽 상단으로 이동\r\n- Random bars horizontal : 수평선이 무작위로 생성\r\n- Random bars vertical : 수직선이 무작위로 생성\r\n- Random : 임의로 생성\r\n"},{"title":"javascript basic 1 num str","description":"javascript 기초 1 숫자열문자열","category":"javascript","date":"2023-03-06","content":"\r\n# 1. 숫자열을 문자열로 바꾸기\r\n\r\n```javascript\r\n\r\n// 숫자는 숫자+\"\"만 해줘도 (혹은 다른 문자를 더해줘도) 문자열로 바뀐다.\r\n\r\nlet a = 1234\r\nconsole.log(a) // a는 숫자 1234\r\nlet b = a+\"\"\r\nconsole.log(b) // b는 문자 '1234'\r\nlet c = 1234.toString()\r\nconsole.log(c) // c는 문자 '1234\r\n\r\n```\r\n\r\n# 2. 문자열을 숫자열로 바꾸기\r\n\r\n```javascript\r\n\r\n//s는 String\r\nNumber(s)로 문자열을 숫자로 변환 - 인자를 바꿀 수 없으면(ex. 123입니다) NaN을 리턴한다.\r\nparseInt(s)로 문자열을 숫자로 변환 - 정수나 NaN을 리턴한다.\r\nparseFloat(s)로 문자열을 숫자로 변환 - 부동 소수점 숫자를 반환한다.\r\n\r\n// parseFloat 예제\r\n\r\nfunction circumference(r) {\r\n return parseFloat(r) * 2.0 * Math.PI;\r\n}\r\n\r\nconsole.log(circumference(4.567));\r\n// 28.695307297889173\r\n\r\nconsole.log(circumference('4.567abcdefgh'));\r\n// 28.695307297889173\r\n\r\nconsole.log(circumference('abcdefgh'));\r\n// NaN\r\n\r\n\r\n```\r\n\r\n# 3. 문자열 다루기\r\n\r\n```javascript\r\n\r\nString.trim() - 문자열의 공백을 제거\r\n .trimStart() - 문자열 왼쪽 공백 제거\r\n .trimEnd() - 오른쪽 공백 제거\r\n .slice(0,-1) - 문자열에서 텍스트를 추출하고 새 문자열을 반환\r\n .split('') - 문자열 String을 ()안 인자(''등)로 나눠 각 글자별로 배열 생성\r\n \t\t// ex) let name=hanbbi; name.split('') // [ 'h', 'a', 'n', 'b', 'b', 'i' ]\r\n .join('') 배열 내 요소들을 인자를 통해 이어준다.\r\n .replace(\"인자1\",\"인자2\") - 문자 내 맨 처음 발견된 인자1을 찾아 인자2로 바꿔준다.\r\n .replace(\"인자1\",\"인자2\") - 문자 내 모든 인자1을 찾아 인자2로 바꿔준다.\r\n .repeat() - 해당 문자를 ()번 반복\r\n .includes() - 문자열이 특정 문자열을 포함하는지 확인\r\n .charAt(0) - 스트링의 0번째 글자 가져오기(result = string)\r\n .substring(0,5) - 스트링의 0~4번째 글자 가져오기\r\n\r\n```\r\n"},{"title":"javascript basic 2 date","description":"javascript 기초 2 날짜다루기","category":"javascript","date":"2023-03-06","content":"\r\n# Javascript 날짜 다루기\r\n\r\n## 날짜 계산\r\n\r\n```javascript\r\n// 날짜 예제\r\n// Sunday - Saturday : 0 - 6\r\nconst someday = new Date(\"2022-01-23\");\r\n// 날짜 형식은 자유로움 ('january 23 ,2022') 처럼 사용 가능\r\n\r\nconst day1 = someday.getDay();\r\nconsole.log(day1); // 22.01.23은 일요일이므로, 0 반환\r\n\r\n// 날짜 추출\r\nlet today = new Date(); // 인자가 비어있는 경우 현재 날짜/시간 반환\r\n\r\nlet year = today?.getFullYear();\r\nconsole.log(year); // 123 - 2023년 반환값 (2022년 반환값 = 122)\r\n\r\nlet month = today?.getMonth() + 1; // javascript에서는 1월을 0부터 계산해주기 떄문에 + 1을 해준다.\r\nconsole.log(month); // 3\r\n\r\nlet day = today?.getDate();\r\nconsole.log(day); // 6\r\n\r\nlet hour = today?.getHour();\r\nconsole.log(hour); // 현재 시간 추출\r\n\r\nlet minutes = today?.getMinutes();\r\nconsole.log(minutes); // 현재 분 추출\r\n\r\nlet seconds = today?.getSeconds();\r\nconsole.log(seconds); // 현재 초 추출\r\n```\r\n"},{"title":"javascript basic 3 math","description":"javascript 기초 3 Math 다뤄보기","category":"javascript","date":"2023-03-06","content":"\r\n# Javascript Math 메서드\r\n\r\n## 1. 소수점 표현\r\n\r\n```javascript\r\n// n = Number\r\nMath.ceil(n); // 올림\r\nMath.floor(n); // 내림\r\nMath.round(n); // 반올림\r\nMath.toFixed(n); // 소수점 n의 자릿수까지 표현 / 매개변수가 없으면 1의자릿수 반환\r\n\r\n// .toFixed 예제\r\nlet num = 10.987654321;\r\n\r\nnum.toFixed(); // 11\r\nnum.toFixed(1); // 11.0\r\nnum.toFixed(2); // 10.99\r\nnum.toFixed(3); // 10.988\r\nnum.toFixed(4); // 10.9877\r\nnum.toFixed(5); // 10.98765\r\nnum.toFixed(10); // 10.9876543210\r\nnum.toFixed(14); // 10.98765432100000\r\n```\r\n\r\n## 2. 계산/정수 처리\r\n\r\n```javascript\r\nMath.abs(); // 절대값 반환\r\nMath.max(); // 인자 중 가장 큰 값 반환 ex. Math.max(1,2,3) - 3 , Math.max([4,5,6]) - 6\r\nMath.min(); // 인자 중 가장 작은 값 반환 - Math.max 반대\r\nMath.pow(x, y); // 인자간 거듭제곱 값 반환 === x**y와 동일\r\nMath.random(); // 0이상 1미만 랜덤값 반환\r\nMath.sqrt(); // 제곱근 반환\r\n```\r\n"},{"title":"javascript basic 4 array 1","description":"javascript 기초 4 배열 다루기 - 1","category":"javascript","date":"2023-03-06","content":"\r\n# JavaScript 배열 다뤄보기\r\n\r\n## 1. 배열 수정\r\n\r\n```javascript\r\nlet Arr = [1, 2, 3, 4, 5];\r\n\r\n// Arr배열의 0번째를 123으로 수정\r\nArr[0] = 123; // Arr = [123,2,3,4,5]\r\n```\r\n\r\n## 2. 배열 정렬 (Array.sort())\r\n\r\narray.sort()는 매개변수가 입력되지 않으면 유니코드 순서에 따라 정렬된다.\r\n\r\n또한 sort()는 원본 array를 참조하기 때문에 sort()를 통한 정렬은 원본 array도 동일하게 변경된다.\r\n\r\n### 숫자 배열 정렬\r\n\r\n```javascript\r\nlet arr = [1, 3, 2, 4];\r\n\r\narr.sort(); // [1,2,3,4]\r\narr.sort((a, b) => a - b); // [1,2,3,4]\r\narr.sort((a, b) => b - a); // [4,3,2,1]\r\n```\r\n\r\n### 문자 배열 정렬 (대소문자 구분x)\r\n\r\n```javascript\r\nlet arr3 = [\"jon\", \"awre\", \"gwgw\", \"zgfzg\"];\r\n\r\n// 오름차순\r\narr.sort(function (a, b) {\r\n const upperCaseA = a.toUpperCase();\r\n const upperCaseB = b.toUpperCase();\r\n\r\n if (upperCaseA > upperCaseB) return 1;\r\n if (upperCaseA < upperCaseB) return -1;\r\n if (upperCaseA === upperCaseB) return 0;\r\n}); // ['awre', 'gwgw', 'jon', 'zgfzg']\r\n\r\n// 내림차순\r\narr.sort(function (a, b) {\r\n const upperCaseA = a.toUpperCase();\r\n const upperCaseB = b.toUpperCase();\r\n\r\n if (upperCaseA < upperCaseB) return 1;\r\n if (upperCaseA > upperCaseB) return -1;\r\n if (upperCaseA === upperCaseB) return 0;\r\n}); // ['zgfzg', 'jon', 'gwgw', 'awre']\r\n```\r\n\r\n### 영어가 아닌 한글 등의 문자 정렬\r\n\r\n```javascript\r\nlet arr = [\"가\", \"나\", \"바\", \"다\"];\r\n\r\narr1.sort((a, b) => a.localeCompare(b)); // ['가', '나', '다', '바']\r\narr1.sort((a, b) => b.localeCompare(a)); // ['바', '다', '나', '가']\r\n```\r\n\r\n## 3. 배열 메서드\r\n\r\n```javascript\r\n\r\nlet Arr = [1,2,3,4,5]\r\n\r\n // 배열의 마지막에 새로운 요소를 추가 / 변경된 배열의 길이를 반환\r\nArr.push(12) // Arr = [1,2,3,4,5,12] / 6\r\n\r\n\t// 배열의 마지막 요소를 제거 / 한 후, 제거한 요소를 반환\r\n .pop() // Arr = [1,2,3,4] / 5\r\n\r\n // 배열의 첫 번째 자리에 새로운 요소를 추가/ 변경된 배열의 길이를 반환\r\n .unshift(12) // Arr = [12,1,2,3,4,5] / 6\r\n\r\n // 배열의 첫 번째 요소를 제거 / 제거한 요소를 반환\r\n .shift() // Arr = [2,3,4,5] / 1\r\n\r\n // 배열의 1번째 index부터 2개 원소 제거\r\n .splice(1, 2) // Arr = [1, 4, 5] / [2, 3] 제거됨\r\n\r\n // 배열 반전\r\n .reverse() 배열 반전 // [5,4,3,2,1]\r\n\r\n // 배열 계산\r\n //Arr.reduce((이전결과, 현재요소)=>이전결과+현재요소, 초기값)\r\n // 초기값이 없는 경우 acc = Arr[0] , cur = Arr[1] 이 된다.\r\n .reduce((acc,cur)=>acc+cur) 배열 계산 // 15\r\n\r\n // 배열 추출\r\n // .slice(x, y) x이상 y미만 index에 해당하는 배열 추출\r\n // 매개변수가 x밖에 없다면 x 이상 index에 해당하는 배열 추출\r\n // 매개변수가 음수인 경우 ex. (-2) 라면, 배열 끝에서 2번째 index 이상의 배열 추출\r\n .slice(0, 2) // [1,2]\r\n\r\n```\r\n"},{"title":"javascript basic 4 array 2","description":"javascript 기초 4 배열 다루기 - 2","category":"javascript","date":"2023-03-06","content":"\r\n# 고차함수와 메서드를 통해 JavaScript 배열 다뤄보기\r\n\r\n## 고차함수란?\r\n\r\n> 함수를 인자로 받거나, 함수를 반환함으로써 작동하는 함수로, .map() , .filter() , .forEach() , .reduce() 등이 있다.\r\n\r\n### 참고\r\n\r\n아래의 메서드들은 모두 원본 배열에 영향을 주지 않는다.\r\n\r\n또한 함수 안의 화살표 함수 arr.filter/map/...((element)=>(element>2)) 와 같이 괄호()를 이용하면 return을 해줄 필요가 없지만, arr.filter/map/...((element)=>{return element > 2}) 처럼 중괄호를 사용하면 return을 꼭 해줘야 한다.\r\n\r\n### Array.filter()\r\n\r\n특정 조건을 부합하는 값만 모아서 새로운 배열을 만들어준다.\r\n\r\n```javascript\r\n// ex\r\nconst arr = [\"a\", \"b\", \"b\", \"c\"];\r\nconst filtered = arr.filter(element => element != \"b\"); // arr의 원소는 b가 아닌 값으로 거른다.\r\nconsole.log(arr); // ['a', 'b', 'b', 'c']\r\nconsole.log(filtered); // ['a', 'c']\r\n\r\nconst arr_num = [0, 1, 2, 3, 4, 5];\r\nconst new_array = arr_num.filter(element => element > 3); // arr_num의 원소는 3을 초과한다\r\nconsole.log(new_array); // [ 4, 5 ]\r\n```\r\n\r\n### Array.map()\r\n\r\n반복문처럼 사용 가능. 원본 배열을 흐트러뜨리지 않고, 새로운 배열을 반환한다.\r\n\r\n단, key 값 같은 경우에는 값이 재할당되기 때문에 원본과 비교하는 의미가 없다. (ex. 리액트 사용 시)\r\n\r\n```javascript\r\nconst array_num = [0, 1, 2, 3, 4, 5]; // arr_n은 상수이지만\r\nconst new_arr_num = array_num.map(\r\n element => element * 10, // 요렇게 map으로 *10을 돌려주면\r\n);\r\nconsole.log(new_arr_num); // [ 0, 10, 20, 30, 40, 50 ] // 원본 배열 변경 없이 새 배열이 잘 만들어진다.\r\n```\r\n\r\n### Array.concat()\r\n\r\n두 배열을 합친다. 단, 중복 제거는 하지 않는다.\r\n\r\n```javascript\r\nconst array_num = [0, 1, 2, 3, 4, 5];\r\nconst new_array = [4, 5];\r\nconst merge = array_num.concat(new_array);\r\nconsole.log(merge); // [0, 1, 2, 3, 4, 5, 4, 5]\r\n```\r\n\r\n### Array.from()\r\n\r\n배열로 만들어준다.\r\n\r\n```javascript\r\nconst my_name = \"vihan9\";\r\nconst my_name_array = Array.from(my_name);\r\nconsole.log(my_name_array); // [ 'v', 'i', 'h', 'a', 'n', '9' ]\r\n\r\n// 조건을 넣어 배열을 만들고 싶다면? ex\r\nconst my_name_array = Array.from({ length: 4 }, (item, index) => index);\r\nconsole.log(my_name_array); // [ 0, 1, 2, 3 ] // length가 4인 배열을 만들어준다.\r\n\r\n// 어떤 배열을 index 값 배열로 바꾸고 싶다면? ex\r\nconst my_name = \"vihan9\";\r\nconst num_array = Array.from(my_name, (item, index) => index);\r\nconsole.log(num_array); // [ 0, 1, 2, 3, 4, 5 ]\r\n```\r\n\r\n### Array.find()\r\n\r\n숫자열 배열의 특정 값을 찾아주며, 조건에 맞는 첫 번째 값을 호출해준다.\r\n\r\n```javascript\r\nconst arr_num = [0, 1, 2, 3, 4, 5];\r\nconst find_num = arr_num.find(v => v > 3);\r\nconsole.log(find_num); // 4 // 3을 초과하는 첫 값인 4를 출력한다.\r\n```\r\n\r\n### indexOf()\r\n\r\n배열에서 지정된 요소를 찾을 수 있는 첫 번째 반환하고, 존재하지 않으면 -1을 반환한다.\r\n\r\n```javascript\r\nconst comment = \"오늘도 오류 없는 하루가 되게 해주세요\";\r\nconst searchStr = \"오류\";\r\nconst searchStr2 = \"해주세요\";\r\n\r\nconst indexOfFirst = comment.indexOf(searchStr);\r\nconst indexOfFirst2 = comment.indexOf(searchStr2);\r\n\r\nconsole.log(indexOfFirst); // 4 // 오류는 4번째 인덱스부터 시작한다.\r\nconsole.log(indexOfFirst2); // 17 // 해주세요는 17번째 인덱스부터 시작한다.\r\n```\r\n"},{"title":"javascript basic 5 loop","description":"javascript 기초 5 반복문 다루기","category":"javascript","date":"2023-03-06","content":"\r\n# for문 사용법\r\n\r\n## for문\r\n\r\n```javascript\r\n\r\nconst arr = [1,2,3,4,5,6,7,8,9,10];\r\n\r\nconst newArr = []\r\n (초기값(i=0) ; 조건(i가 10 미만까지 반복) ; 증감문(i는 1씩 증가한다.))\r\nfor(let i = 0;iconsole.log(element));\r\n\r\n// console.log\r\n1 2 3 4 5 6 7 8 9 10\r\n\r\n// 간단 예제\r\n\r\narr.forEach((element)=>console.log(element*10)); // 10 20 30 40 --- 80 90 100\r\n\r\n// for문처럼 화살표 함수 오른쪽 부분에 push 등과 같은 메서드를 통해 새 배열을 만들어줄 수도 있다.\r\nconst newArr = [];\r\narr.forEach((item)=>{newArr.push(item)});\r\n// newArr =\r\n[\r\n 1, 2, 3, 4, 5,\r\n 6, 7, 8, 9, 10\r\n]\r\n\r\n```\r\n\r\n## .map()\r\n\r\nforEach와 유사하게 배열 안의 각 요소들에게 콜백 함수를 적용시킨다.\r\n\r\n```javascript\r\nconst arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\r\n\r\nlet ex = arr.map(item => item * 10)[\r\n //console.log(ex)\r\n (10, 20, 30, 40, 50, 60, 70, 80, 90, 100)\r\n];\r\n\r\nconst arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\r\n\r\nlet ex = arr.map((item, index) => item * 10 + `(${index}번째)`)[\r\n //console.log(ex)\r\n (\"10(0번째)\",\r\n \"20(1번째)\",\r\n \"30(2번째)\",\r\n \"40(3번째)\",\r\n \"50(4번째)\",\r\n \"60(5번째)\",\r\n \"70(6번째)\",\r\n \"80(7번째)\",\r\n \"90(8번째)\",\r\n \"100(9번째)\")\r\n];\r\n```\r\n"},{"title":"nestjs custom repository","description":"Entityrepository()를 대신한 Repository 생성","category":"nestjs","date":"2023-03-04","content":"\r\n## 포스팅 시점 nestjs 버전 : 9.x.x\r\n\r\n새로운 토이 프로젝트를 만들면서 백엔드를 구축할 프레임워크로 nestjs를 이용하기로 마음먹었다.\r\n\r\n원래 사용하고 있던 javascript/typescript와 동일한 언어를 사용하기 때문에 백엔드 구축에 비교적 진입장벽이 낮다고 생각했기 때문에다.\r\n\r\n# nest 작동순서\r\n\r\nrequest -> controller -> service -> controller -> response 순으로 작동하나, DB와 관련된 일을 시키기 위해서는 repository를 생성해야한다고 한다. 즉,\r\n\r\nrequest -> controller -> service -> repo\\* -> service -> controller -> response\r\n로 한 단계가 추가된다. (Repostitory pattern)\r\n\r\n@EntityRepository를 통해 레포지토리를 설정해주는 방식이 많이 보였지만\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlkldE%2FbtrUQbTjKZA%2FA6hFzyRy8SsueBbUk8cTA1%2Fimg.png)\r\n\r\n현재 버전에서는 사용할 수 없어 다른 방법을 알아봤다.\r\n\r\n## 기본 설정\r\n\r\nconfig 설정\r\n\r\n```typescript\r\n// src/configs/typeorm.configs.ts\r\n\r\nexport const typeORMConfig: TypeOrmModuleOptions = {\r\n // Database Type\r\n type: \"postgres\", // 본인은 pg를 사용\r\n host: \"localhost\",\r\n port: 5432,\r\n username: \"postgres\",\r\n password: \"password\",\r\n database: \"database\",\r\n entities: [__dirname + \"/../**/*.entity.{js,ts}\"], // 해당 경로 + 엔티티이름.entity.{js.ts}로 된 엔티티를 이용\r\n // autoLoadEntities: true, // 위와 다르게 entity들 알아서 찾아줌\r\n synchronize: true, // @@@@@@@@ 배포할때 true 사용 시 데이터 삭제될 수 있음. production 단계에서는 필히 false로 해줄 것 권장 @@@@@@\r\n};\r\n```\r\n\r\nconfig 설정 후 root 모듈에 import\r\n\r\n```typescript\r\n// app.module.ts\r\n\r\nimport { Module } from \"@nestjs/common\";\r\nimport { AppController } from \"./app.controller\";\r\nimport { PostlistModule } from \"./postlist/postlist.module\";\r\nimport { typeORMConfig } from \"./configs/typeorm.configs\";\r\nimport { TypeOrmModule } from \"@nestjs/typeorm/dist\";\r\n\r\n@Module({\r\n imports: [TypeOrmModule.forRoot(typeORMConfig), PostlistModule],\r\n controllers: [AppController],\r\n providers: [],\r\n})\r\nexport class AppModule {}\r\n```\r\n\r\nentity 생성\r\n\r\n```typescript\r\n// src/postlist/entities/postlist.entity.ts\r\n\r\nimport { BaseEntity, Column, PrimaryGeneratedColumn, Entity } from \"typeorm\";\r\n\r\n@Entity()\r\nexport class Postlist extends BaseEntity {\r\n @PrimaryGeneratedColumn()\r\n id: number;\r\n\r\n @Column()\r\n title: string;\r\n\r\n @Column()\r\n content: string;\r\n\r\n @Column()\r\n nickname: string;\r\n\r\n @Column()\r\n imageUrl?: string;\r\n}\r\n```\r\n\r\n> config 설정 시 entities에 이름을 ~~.entity.{js.ts} 로 설정해놓고, 정작 entity 파일 이름을 설정한 형식과 다르게 작성하면 [EntityMetadataNotFound] 에러를 띄우게 되기 때문에 설정한대로 이름을 지어줘야 한다.\r\n\r\n참조 : https://github.com/typeorm/typeorm/issues/1327\r\n\r\n## custumRepository 만들기\r\n\r\n본인은 src에 database 디렉토리를 만들어, 데코레이터와 모듈을 그 안에 넣어줬다.\r\n\r\n```typescript\r\n// src/database/typeorm-ex.decorator.ts\r\n\r\nimport { SetMetadata } from \"@nestjs/common\";\r\n\r\nexport const TYPEORM_EX_CUSTOM_REPOSITORY = \"TYPEORM_EX_CUSTOM_REPOSITORY\";\r\n\r\nexport function CustomRepository(entity: Function): ClassDecorator {\r\n return SetMetadata(TYPEORM_EX_CUSTOM_REPOSITORY, entity);\r\n}\r\n\r\n// typeorm-ex.module.ts\r\n\r\nimport { DynamicModule, Provider } from \"@nestjs/common\";\r\nimport { getDataSourceToken } from \"@nestjs/typeorm\";\r\nimport { DataSource } from \"typeorm\";\r\nimport { TYPEORM_EX_CUSTOM_REPOSITORY } from \"./typeorm-ex.decorator\";\r\n\r\nexport class TypeOrmExModule {\r\n public static forCustomRepository any>(\r\n repositories: T[],\r\n ): DynamicModule {\r\n const providers: Provider[] = [];\r\n\r\n for (const repository of repositories) {\r\n const entity = Reflect.getMetadata(\r\n TYPEORM_EX_CUSTOM_REPOSITORY,\r\n repository,\r\n );\r\n\r\n if (!entity) {\r\n continue;\r\n }\r\n\r\n providers.push({\r\n inject: [getDataSourceToken()],\r\n provide: repository,\r\n useFactory: (dataSource: DataSource): typeof repository => {\r\n const baseRepository = dataSource.getRepository(entity);\r\n return new repository(\r\n baseRepository.target,\r\n baseRepository.manager,\r\n baseRepository.queryRunner,\r\n );\r\n },\r\n });\r\n }\r\n\r\n return {\r\n exports: providers,\r\n module: TypeOrmExModule,\r\n providers,\r\n };\r\n }\r\n}\r\n```\r\n\r\n## 사용할 repository 생성\r\n\r\n```typescript\r\n// src/postlist/repository/postlist.repository.ts\r\n\r\nimport { CustomRepository } from \"src/database/typeorm-ex.decorator\";\r\nimport { Repository } from \"typeorm\";\r\nimport { Postlist } from \"../entities/postlist.entity\";\r\n\r\n@CustomRepository(Postlist)\r\nexport class PostlistRepository extends Repository {}\r\n```\r\n\r\n## module에 생성한 repository import\r\n\r\n```typescript\r\n// src/postlist/postlist.module.ts\r\n\r\nimport { Module } from \"@nestjs/common\";\r\nimport { PostlistController } from \"./postlist.controller\";\r\nimport { PostlistService } from \"./postlist.service\";\r\nimport { PostlistRepository } from \"./repository/postlist.repository\";\r\nimport { TypeOrmExModule } from \"src/database/typeorm-ex.module\";\r\n\r\n@Module({\r\n imports: [TypeOrmExModule.forCustomRepository([PostlistRepository])],\r\n controllers: [PostlistController],\r\n providers: [PostlistService],\r\n})\r\nexport class PostlistModule {}\r\n```\r\n\r\n### Module에 import 후 service 비즈니스 로직에만 repository를 넣어주면 된다.\r\n\r\n```typescript\r\n// src/postlist/postlist.service.ts\r\n\r\nimport { Injectable, NotFoundException } from \"@nestjs/common\";\r\nimport { UpdatePostDto } from \"./dto/update-postlist.dto\";\r\n\r\nimport { PostlistRepository } from \"./repository/postlist.repository\";\r\nimport { Postlist } from \"./entities/postlist.entity\";\r\nimport { CreatePostDto } from \"./dto/create-postlist.dto\";\r\n\r\n@Injectable()\r\nexport class PostlistService {\r\n constructor(private readonly postlistRepository: PostlistRepository) {}\r\n\r\n async getAllPost(): Promise {\r\n const found = await this.postlistRepository.find();\r\n return found;\r\n }\r\n\r\n async getPostById(id: number): Promise {\r\n const found = await this.postlistRepository.findOne({ where: { id: id } });\r\n if (!found) {\r\n throw new NotFoundException(`Cannot find post with id ${id}`);\r\n }\r\n return found;\r\n }\r\n}\r\n\r\n// src/postlist/postlist.controller.ts\r\n\r\nimport { Body, Controller, Get, Param, Patch, Post } from \"@nestjs/common\";\r\nimport { CreatePostDto } from \"./dto/create-postlist.dto\";\r\nimport { PostlistService } from \"./postlist.service\";\r\nimport { UpdatePostDto } from \"./dto/update-postlist.dto\";\r\nimport { Delete } from \"@nestjs/common/decorators\";\r\nimport { Postlist } from \"./entities/postlist.entity\";\r\n\r\n@Controller(\"postlist\")\r\nexport class PostlistController {\r\n constructor(readonly postlistService: PostlistService) {}\r\n\r\n @Post()\r\n createOne(@Body() createPostDto: CreatePostDto): Promise {\r\n return this.postlistService.createPost(createPostDto);\r\n }\r\n\r\n @Get()\r\n getAll(): Promise {\r\n return this.postlistService.getAllPost();\r\n }\r\n\r\n @Get(\":id\")\r\n getOne(@Param(\"id\") id: number): Promise {\r\n return this.postlistService.getPostById(id);\r\n }\r\n\r\n @Patch(\":id\")\r\n updateOne(\r\n @Param(\"id\") id: number,\r\n @Body() updateData: UpdatePostDto,\r\n ): Promise {\r\n return this.postlistService.updatePost(id, updateData);\r\n }\r\n\r\n @Delete(\":id\")\r\n deleteOne(@Param(\"id\") id: number): Promise {\r\n return this.postlistService.deletePost(id);\r\n }\r\n}\r\n```\r\n\r\n필요한 로직을 설정한 후 postman을 작동시켜주면 정상적으로 실행되는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMxspb%2FbtrUIZG6vWP%2F0dVetFuaUfkfuI9EXCKeN0%2Fimg.png)\r\n\r\nnextjs와 동시에 작업하기 위해 nestjs 포트를 3001로 설정했지만, 보통은 nest 설치 시 app에 3000으로 설정된다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOfHG5%2FbtrUOQBYHjX%2FoGOqOJtiF2KbPiyKu9JCdK%2Fimg.png)\r\n\r\nDB는 postgreSQL을 이용했다.\r\n정상적으로 테이블에 있는 데이터가 추출되는 것이 잘 확인된다.\r\n\r\n### 참고 :\r\n\r\nhttps://orkhan.gitbook.io/typeorm/readme_ko\r\n\r\nhttps://docs.nestjs.com/techniques/database\r\n"},{"title":"nextjs loading","description":"nextjs에서의 로딩처리","category":"nextjs","date":"2023-03-04","content":"\r\n# Nextjs에서 로딩처리하기\r\n\r\n### 모든 페이지를 미리 렌더링하는 NextJS 특성상, 다른 페이지로 라우팅이 진행될때, 사용자는 가만히 멈춰있는 화면을 보게될 수 있다. 때문에 페이지 전환을 정지화면으로 두지 않기 위해 로딩 처리를 구현해볼 수 있다.\r\n\r\n## 로딩 스피너 세팅\r\n\r\n```typescript\r\n// _app.tsx\r\n\r\n// 미리 만들어놓은 로딩 훅과 로딩 스피너\r\nimport { useLoading } from \"src/hooks/useLoading\";\r\nimport { LoadingSpinner } from \"src/components/videos/video/LoadingSpinner\";\r\n\r\n\r\n {isLoading ? : null}\r\n \r\n;\r\n```\r\n\r\n로딩스피너는 CSS로 만들어도 좋고, SVG나 GIF, 라이브러리 등 자유롭게 적용해도 좋다.\r\n\r\n## 라우팅 시 적용될 이벤트 설정\r\n\r\n```typescript\r\n// useLoading.ts\r\n\r\nimport Router from \"next/router\";\r\nimport { useEffect, useState } from \"react\";\r\n\r\nexport const useLoading = () => {\r\n const [nowLoading, setNowLoading] = useState(false);\r\n useEffect(() => {\r\n const start = () => {\r\n setNowLoading(true);\r\n };\r\n const end = () => {\r\n setNowLoading(false);\r\n };\r\n Router.events.on(\"routeChangeStart\", start);\r\n Router.events.on(\"routeChangeComplete\", end);\r\n Router.events.on(\"routeChangeError\", end);\r\n return () => {\r\n Router.events.off(\"routeChangeStart\", start);\r\n Router.events.off(\"routeChangeComplete\", end);\r\n Router.events.off(\"routeChangeError\", end);\r\n };\r\n }, []);\r\n\r\n return nowLoading ? true : false;\r\n};\r\n```\r\n\r\n- routeChangeStart(url, { shallow }) - 라우트가 변경되기 시작할때 트리거됨.\r\n\r\n- routeChangeComplete(url, { shallow }) - 라우트가 완전히 변경되었을 때 트리거됨.\r\n\r\n- routeChangeError(err, url, { shallow }) - 라우트 변경 중에 에러가 발생했거나, 취소되었을 때 트리거됨.\r\n\r\n\\_app.tsx(jsx)에 로딩 스피너나 로딩 페이지를 적용해 놓으면,\r\n\r\n라우팅으로 인한 페이지 이동이 일어날 때마다 원하는 로딩 창을 호출해줄 수 있다.\r\n\r\n## 적용된 페이지\r\n\r\n![image](https://blog.kakaocdn.net/dn/cWz72g/btrSmvngm4A/hgKg4FviDpAqQk5U6kGHXK/img.gif)\r\n"},{"title":"nextjs react responsive","description":"nextjs에 react responsive 적용하기","category":"nextjs","date":"2023-03-04","content":"\r\n### 이번에 새로 만들고 있는 토이 프로젝트를 작업하던 중, 모바일 화면일때만 화면을 보여주고 싶었다. 단, 모든 컴포넌트에 미디어쿼리를 통해 작성해줄 수는 없기에 react-responsive를 적용해 화면이 481px을 넘어가면 고정된 페이지를 보여줄 수 있도록 코드를 작성했다.\r\n\r\n## react-responsive 설치\r\n\r\n```bash\r\nyarn add react-responsive\r\n```\r\n\r\n## hook 설정\r\n\r\n```typescript\r\n// hook\r\n// useMideaQuery.tsx\r\nexport const Desktop = ({ children }: any) => {\r\n const isDesktop = useMediaQuery({\r\n minWidth: 481,\r\n });\r\n return isDesktop ? children : null;\r\n};\r\n\r\nexport const Mobile = ({ children }: any) => {\r\n const isMobile = useMediaQuery({\r\n minWidth: 481,\r\n });\r\n return Mobile ? children : null;\r\n};\r\n```\r\n\r\n## \\_app.ts 적용\r\n\r\n```typescript\r\n// _app.tsx\r\n\r\n// ...\r\nimport { Mobile } from \"src/hooks/useMideaQuery\";\r\nimport { CannotDesktop } from \"src/desktop/CannotDesktop\";\r\n\r\n<>\r\n \r\n \r\n {isLoading ? : null}\r\n \r\n \r\n \r\n \r\n \r\n \r\n;\r\n```\r\n\r\n똑같은 코드로 리액트 코드에 적용할 때는 문제가 없었지만, Nextjs를 이용한 이번 상황의 경우 Hydration 에러가 발생했다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjYUXg%2FbtrTRCeaovE%2FwQr4OZdJectnkkG06XmTEk%2Fimg.png)\r\n\r\nvercel로 배포할 때는 정상적으로 빌드되기는 했지만\r\n\r\n로컬에서 작업할때 지속적으로 발생하는 오류 메세지 창이 불편하기도 하여 바로 처리해봤다.\r\n\r\n> Hydration failed because the initial UI does not match what was rendered on the server\r\n\r\n초기 UI가 서버에서 렌더링된 것과 일치하지 않는다고 한다.\r\n\r\nhttps://nextjs.org/docs/messages/react-hydration-error\r\n\r\n공식 페이지를 보면 \"애플리케이션을 렌더링하는 동안 미리 렌더링된 React 트리(SSR/SSG)와 브라우저에서 첫 번째 렌더링 중에 렌더링된 React 트리 간에 차이가 있었기 때문에 발생한 오류\"라고 한다.\r\n\r\n일반적으로 이 문제는 사전 렌더링과 브라우저 간에 다를 수 있는 항목에 의존하는 특정 라리브러리 또는 애플리케이션 코드를 사용하여 발생한다고 하는데,\r\n\r\n나의 경우, react-responsive에 의한 경우였다.\r\n\r\n## 코드 수정\r\n\r\n```typescript\r\n// useMideaQuery.tsx\r\n\r\nimport { useEffect, useState } from \"react\";\r\nimport { useMediaQuery } from \"react-responsive\";\r\n\r\nexport const Mobile = () => {\r\n const [mobile, setMobile] = useState(false);\r\n const isMobile = useMediaQuery({ maxWidth: 480 });\r\n\r\n const checkResize = () => {\r\n if (isMobile) {\r\n setMobile(true);\r\n } else {\r\n setMobile(false);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n checkResize();\r\n }, [isMobile]);\r\n\r\n return mobile;\r\n};\r\n//_app.tsx\r\n\r\n\r\n {isMobile ? (\r\n \r\n {isLoading ? : null}\r\n \r\n \r\n ) : (\r\n \r\n )}\r\n;\r\n```\r\n\r\nuseMideaQuery가 window 화면을 기준으로 동작하기 때문에,\r\n\r\n공식문서의 예제와 비슷하게 useState를 통한 boolean 값으로 화면을 노출시켜줬다.\r\n\r\n위의 방식을 적용하니 hydration 에러 없이 잘 작동하는 모습을 볼 수 있었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdH0CZg%2FbtrTRp0izJZ%2FiolwpQXLvGOommEBhpCkaK%2Fimg.png)\r\n"},{"title":"redux mock server","description":"postman을 이용해 redux Mock server(json-server) 사용해보기","category":"redux","date":"2023-03-04","content":"\r\n### React를 사용하면서 서버와 연결 전, Post Man을 통해 임시로 서버를 연결해 React와 소통하는 데이터와 view가 어떻게 처리되는지 확인하고 싶을때 사용해볼 수 있다.\r\n\r\npostman 링크 : https://www.postman.com/\r\n\r\n## json-server 설치\r\n\r\n```bash\r\nyarn add json-server\r\n```\r\n\r\n위 명령어 입력 시 db.json 파일이 자동으로 생성된다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fle0HO%2FbtrMjcfRkOG%2FPMsBpdgzHf6545oXRlN8BK%2Fimg.png)\r\n\r\n설치가 완료된 후 아래의 명령어를 입력하면 json server가 실행된다.\r\n\r\n## json-server 실행\r\n\r\n```bash\r\nyarn json-server --watch db.json --port 3001\r\n```\r\n\r\n> json-server를 설치해줄때 생성된 db.json 파일을 실행하는데, 3001번 포트를 사용한다는 의미이다. 포트를 설정해주지 않으면 기본값은 3000이다.\r\n\r\n> 보통 React를 로컬로 실행시킬때의 포트도 3000이기 때문에 3001번으로 열어줬다. 포트번호는 3001이든 4000이든 5000이든 사용하고 있는 포트번호만 아니면 된다.\r\n\r\n## postman 사용\r\n\r\n이제 임시 DB를 사용하고 있기 때문에 Post Man을 설치하거나, 웹에 로그인을 한 후, json-server가 실행된 상태로 post man에 적용하고자 하는 url을 입력해주면 GET, POST, PUT, DELETE 등 다양한 요청을 처리할 수 있게 된다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl4yDj%2FbtrMkdzirLp%2FweLBKktEnHFX5QcT4NHTK1%2Fimg.png)\r\n\r\n사용되는 url은 db.json에 있는 목록명을 기준으로 적용된다.\r\n\r\n```javascript\r\n\r\n// ex\r\n\r\n{\r\n \"comments\": [\r\n {\r\n \"id\": 1,\r\n \"profile_url\": \"https://picsum.photos/id/1/50/50\",\r\n \"author\": \"abc_1\",\r\n \"content\": \"UI 테스트는 어떻게 진행하나요\",\r\n \"createdAt\": \"2022-03-01\"\r\n },\r\n {\r\n \"id\": 2,\r\n \"profile_url\": \"https://picsum.photos/id/2/50/50\",\r\n \"author\": \"abc_2\",\r\n \"content\": \"막히면 대답은 빨리 해주나요\",\r\n \"createdAt\": \"2022-03-02\"\r\n },\r\n ],\r\n \"post\": [\r\n {\r\n \"id\": 1,\r\n \"author\": \"abc_1\",\r\n \"content\": \"UI 테스트는 어떻게 진행하나요\",\r\n \"createdAt\": \"2022-03-01\"\r\n }\r\n {\r\n \"id\": 2,\r\n \"author\": \"abc_2\",\r\n \"content\": \"막히면 대답은 빨리 해주나요\",\r\n \"createdAt\": \"2022-03-01\"\r\n }\r\n ]\r\n}\r\n\r\n```\r\n\r\n예를 들어 db.json이 위와 같이 돼있는 상태라면\r\n\r\n### http://localhost:3001/post - post 데이터\r\n\r\n1. GET : http://localhost:3001/post - 전체 post 목록 불러오기\r\n2. GET : http://localhost:3001/post/1 - id가 1인 post 불러오기\r\n3. POST : http://localhost:3001/post - 새 post 생성(저장)\r\n4. PUT : http://localhost:3001/post/1 - id가 1인 post 수정하기\r\n5. DELETE : http://localhost:3001/post/1 - id가 1인 post 삭제하기\r\n\r\n### http://localhost:3001/comments - comments 데이터\r\n\r\n1. GET : http://localhost:3001/comments - 전체 comments 목록 불러오기\r\n2. GET : http://localhost:3001/comments/1 - id가 1인 comments 불러오기\r\n3. POST : http://localhost:3001/comments - 새 comments 생성(저장)\r\n4. PUT : http://localhost:3001/comments/1 - id가 1인 comments 수정하기\r\n5. DELETE : http://localhost:3001/comments/1 - id가 1인 comments 삭제하기\r\n\r\n위와 같은 방식으로 기본적인 동작을 사용해볼 수 있고, header 값이나 body 값 설정 등 다양한 작업을 진행해볼 수 있다.\r\n\r\nRedux를 사용할때, 해당 데이터들을 추출하여 전역 상태로 관리해보는 등의 작업을 실행할 수 있다.\r\n"},{"title":"redux toolkit async thunk","description":"React reduxToolkit과 미들웨어 AsyncThunk 사용해보기","category":"redux","date":"2023-03-04","content":"\r\n# ReduxToolkit\r\n\r\n리덕스 툴킷은 일반적인 리덕스보다 store 설정을 용이하게 해주고, 추가적인 패키지 설치가 필요하지 않게 설계되었으며, 보일러플레이트(여러 군데에서 반복되는 코드)를 최소화하기 위해 만들어졌다.\r\n\r\n더 사용하기 쉽다는 의미이다. 이번 글에서는 미들웨어인 AsyncThunk와 함께 사용하는 방법을 확인할 수 있다.\r\n\r\n## 1. 설치\r\n\r\n```bash\r\nyarn add @reduxjs/toolkit react-redux\r\n```\r\n\r\n## 2. store 생성\r\n\r\n```javascript\r\n// src/redux/store.js\r\n\r\nimport { configureStore } from \"@reduxjs/toolkit\";\r\nimport posts from \"./modules/postSlice\"; // 만들어줄 Slice\r\n\r\nconst store = configureStore({\r\n reducer: {\r\n posts,\r\n },\r\n});\r\nexport default store;\r\n```\r\n\r\n## 3. 최상위 파일(index.js, App.js 등)에 Provider import해오기\r\n\r\n```javascript\r\n// import -- 기존 코드들 --\r\nimport { Provider } from \"react-redux\";\r\nimport store from \"./redux/store\";\r\n\r\nconst root = ReactDOM.createRoot(document.getElementById(\"root\"));\r\nroot.render(\r\n \r\n \r\n ,\r\n);\r\n```\r\n\r\n## 4. 슬라이스 생성\r\n\r\n2번의 store에 import 해줄 파일이다.\r\n\r\n```javascript\r\n// postSlice.js\r\n\r\nimport { createAsyncThunk, createSlice } from \"@reduxjs/toolkit\";\r\nimport axios from \"axios\";\r\n\r\nconst BASE_URL = \"http://localhost:3001\";\r\n\r\nconst initialState = {\r\n // data, isLoading, error로 상태관리\r\n posts: [],\r\n isLoading: false,\r\n error: null,\r\n};\r\n\r\nexport const getPosts = createAsyncThunk(\r\n \"GET_ALL_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n const { data } = await axios.get(`${BASE_URL}/posts`);\r\n return thunkAPI.fulfillWithValue(data);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const addPosts = createAsyncThunk(\r\n \"POST_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n const { data } = await axios.post(`${BASE_URL}/posts`, payload);\r\n console.log(\"data\", data);\r\n return thunkAPI.fulfillWithValue(data);\r\n } catch (errer) {\r\n return thunkAPI.rejectWithValue(errer);\r\n }\r\n },\r\n);\r\n\r\nexport const updatePosts = createAsyncThunk(\r\n \"UPDATAE_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n console.log(payload);\r\n const { data } = await axios.put(\r\n `${BASE_URL}/posts/${payload.id}`,\r\n payload,\r\n );\r\n console.log(\"data\", DataTransfer);\r\n return thunkAPI.fulfillWithValue(data.data);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const deletePosts = createAsyncThunk(\r\n \"DELETE_posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n await axios.delete(`${BASE_URL}/posts/${payload}`);\r\n return thunkAPI.fulfillWithValue(payload);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const postsSlice = createSlice({\r\n name: \"posts\",\r\n initialState,\r\n reducers: {},\r\n extraReducers: {\r\n /* Pending */\r\n [getPosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n [addPosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n [deletePosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n /* Fulfilled */\r\n [getPosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n console.log(action);\r\n state.posts = [...action.payload];\r\n },\r\n [addPosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n state.posts.push(action.payload);\r\n },\r\n [updatePosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n console.log(action);\r\n const newState = state.posts.map(item =>\r\n action.meta.arg.id === item.id\r\n ? { ...item, content: action.meta.arg.content }\r\n : item,\r\n );\r\n state.posts = newState;\r\n return state;\r\n },\r\n [deletePosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n const newState = state.posts.filter(item => item.id !== action.meta.arg);\r\n state.posts = newState;\r\n return state;\r\n },\r\n /* Rejected */\r\n [getPosts.rejected]: (state, action) => {\r\n state.isLoading = false;\r\n state.error = action.payload;\r\n },\r\n },\r\n});\r\n\r\nexport default postsSlice.reducer;\r\n```\r\n\r\n이 상태에서 Postman을 통해 json 서버를 이용하기 때문에 BASE_URL은 로컬 경로로 설정해줬다.\r\n\r\nextraReducers 의 pending, fulfilled, rejected는 각각 대기중/성공/실패 정도로 생각하면 된다.\r\n\r\n아래와 같이 PostMan을 통해 db.json에 데이터가 정상적으로 확인되면\r\n\r\n포스트맨 사용법 : https://lee-yo-han.github.io/redux/redux와-mock-server-사용하기\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fri6Yi%2FbtrMjiBl1Va%2FlCNsQyz5vPTIhIfBFfpC41%2Fimg.png)\r\n\r\n아래와 같이 Redux hook을 통해 데이터를 자유롭게 사용할 수 있다.\r\n\r\n```javascript\r\nimport { getPosts } from \"../redux/modules/postSlice\";\r\nimport { useEffect } from \"react\";\r\nimport { useDispatch, useSelector } from \"react-redux\"; // Redux hooks\r\n\r\nexport default function Home() {\r\n const data = useSelector(state => state.posts.posts); // postSlice에 있는 전역 State 가져오기\r\n const dispatch = useDispatch(); //dispatch 사용 준비\r\n\r\n useEffect(() => {\r\n dispatch(getPosts()); // dispatch 사용 dispatch(액션함수())\r\n }, []);\r\n\r\n console.log(data);\r\n\r\n return (\r\n
\r\n

redux랑 포스팅CRUD

\r\n

데이터를 주세욥

\r\n
    \r\n {data.map(item => (\r\n
  • \r\n {item.id}\r\n {item.nickname}\r\n {item.content}\r\n
  • \r\n ))}\r\n
\r\n
\r\n );\r\n}\r\n```\r\n\r\n위의 코드에서는 액션 함수의 인자(괄호())가 비어있지만, Slice에 원하는 액션 함수들을 작성한 후 dispatch를 해줄때 데이터가 필요한 경우는 아래의 순서로 데이터가 처리된다고 생각하면 좋다.\r\n\r\n### 데이터 처리 순서\r\n\r\n```javascript\r\n\r\n// jsx component\r\ndispath(액션함수(id등의 데이터))\r\n\r\n// => postSlice 파일 내의 액션 함수\r\nexport const addPosts = createAsyncThunk(\r\n \"POST_Posts\",\r\n async (payload, thunkAPI) => { // payload를 통해 데이터를 받고 아래 코드에서 처리할 수 있도록 함\r\n try {\r\n const { data } = await axios.post(`${BASE_URL}/posts`, payload);\r\n console.log(\"data\", data);\r\n return thunkAPI.fulfillWithValue(data); // 요청 성공 부분\r\n } catch (errer) {\r\n return thunkAPI.rejectWithValue(errer);\r\n }\r\n }\r\n );\r\n\r\n// => extraReducers\r\nexport const postsSlice = createSlice({\r\n name: \"posts\",\r\n initialState,\r\n reducers: {},\r\n extraReducers: {\r\n // ---- 생략 ----\r\n /* Fulfilled */\r\n [addPosts.fulfilled]: (state, action) => { // 요청 성공 시, action 인자를 통해 state(전역 상태)를 관리해줄 수 있음 (ex action.payload .---)\r\n state.isLoading = false;\r\n state.posts.push(action.payload);\r\n },\r\n // ---- 생략 ----\r\n```\r\n\r\n데이터가 정상적으로 화면으로 출력되는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKVbGZ%2FbtrMljZJHmV%2F3XHhLVMqFgXEAjz2HqKky0%2Fimg.png)\r\n\r\n단, useSelector를 사용할 때, (state)=>state... 부분은 데이터에 따라 값의 구조가 다를 수 있기 때문에, console.log()를 통해 데이터를 어떻게 받아오는지 확인하며 코드를 작성해주자.\r\n\r\n## 간단한 CRUD 적용 예시\r\n\r\n![image](https://blog.kakaocdn.net/dn/bDrG2e/btrMjWLhmxB/gsemxzlV7oyCzREe8oHOM1/img.gif)\r\n\r\n## AsyncThunk를 사용한다는 것은\r\n\r\n공식문서 : https://redux-toolkit.js.org/api/createAsyncThunk\r\n\r\n공식 문서에 반환된 Promise를 기반으로 LifeCycle 작업을 전달하는 Thunk 작업 생성자를 반환해 비동기 요청 LifeCycle을 처리하기 위한 표준 권장 접근 방식을 추상화한다는 내용이 있다\r\n\r\n이것은 위의 데이터 처리 순서로 설명한 것과 유사하다.\r\n\r\n```javascript\r\n\r\nexport const addPosts = createAsyncThunk(\r\n \"POST_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n const { data } = await axios.post(`${BASE_URL}/posts`, payload);\r\n console.log(\"data\", data);\r\n return thunkAPI.fulfillWithValue(data);\r\n } catch (errer) {\r\n return thunkAPI.rejectWithValue(errer);\r\n }\r\n }\r\n );\r\n\r\n ↑↑↑ 이 부분에서 Promise를 반환해주고\r\n\r\n\r\n\r\n\r\n ↓↓↓↓ 이 부분에서 대기/성공/실패 로 구분해 데이터를 처리해준다\r\n\r\n //Panding\r\n //Fulfilled\r\n ...\r\n\r\n [addPosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n state.posts.push(action.payload); // 게시글을 추가했을때 처리해주는 방식\r\n },\r\n\r\n ...\r\n //Rejected\r\n\r\n```\r\n\r\n# 전체 예제코드\r\n\r\n```javascript\r\n// Home.jsx\r\n\r\nimport {\r\n deletePosts,\r\n getPosts,\r\n updatePosts,\r\n addPosts,\r\n} from \"../redux/modules/postSlice\";\r\n\r\nimport { useState, useEffect } from \"react\";\r\nimport { useDispatch, useSelector } from \"react-redux\";\r\n\r\nexport default function Home() {\r\n const [inputValue, setInputValue] = useState();\r\n\r\n const data = useSelector(state => state.posts.posts);\r\n const dispatch = useDispatch();\r\n useEffect(() => {\r\n dispatch(getPosts());\r\n }, []);\r\n console.log(data);\r\n\r\n const addButton = () => {\r\n let addData = {\r\n nickname: \"한삐\",\r\n content: inputValue,\r\n };\r\n dispatch(addPosts(addData));\r\n };\r\n\r\n const deletButton = props => {\r\n dispatch(deletePosts(props));\r\n };\r\n\r\n const editButton = props => {\r\n let updateData = {\r\n id: props,\r\n nickname: \"한삐\",\r\n content: \"냠냠1231241214\",\r\n };\r\n dispatch(updatePosts(updateData));\r\n };\r\n\r\n return (\r\n
\r\n

redux랑 포스팅CRUD

\r\n

데이터를 주세욥

\r\n setInputValue(e.target.value)} type=\"text\" />\r\n \r\n
    \r\n {data.map(item => (\r\n
  • \r\n
    \r\n {item.id}\r\n
    \r\n {item.nickname}\r\n
    \r\n {item.content}\r\n
    \r\n
    \r\n
    \r\n \r\n \r\n
    \r\n
  • \r\n ))}\r\n
\r\n
\r\n );\r\n}\r\n```\r\n\r\n```javascript\r\n// store.js\r\nimport { configureStore } from \"@reduxjs/toolkit\";\r\nimport posts from \"./modules/postSlice\";\r\n\r\nconst store = configureStore({\r\n reducer: {\r\n posts,\r\n },\r\n});\r\n\r\nexport default store;\r\n```\r\n\r\n```javascript\r\n// postSlice.js\r\n\r\nimport { createAsyncThunk, createSlice } from \"@reduxjs/toolkit\";\r\nimport { current } from \"@reduxjs/toolkit\";\r\nimport axios from \"axios\";\r\n\r\nconst BASE_URL = \"http://localhost:3001\";\r\n\r\nconst initialState = {\r\n // data, isLoading, error로 상태관리\r\n posts: [],\r\n isLoading: false,\r\n error: null,\r\n};\r\n\r\nexport const getPosts = createAsyncThunk(\r\n \"GET_ALL_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n const { data } = await axios.get(`${BASE_URL}/posts`);\r\n return thunkAPI.fulfillWithValue(data);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const addPosts = createAsyncThunk(\r\n \"POST_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n const { data } = await axios.post(`${BASE_URL}/posts`, payload);\r\n console.log(\"data\", data);\r\n return thunkAPI.fulfillWithValue(data);\r\n } catch (errer) {\r\n return thunkAPI.rejectWithValue(errer);\r\n }\r\n },\r\n);\r\n\r\nexport const updatePosts = createAsyncThunk(\r\n \"UPDATAE_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n console.log(payload);\r\n const { data } = await axios.put(\r\n `${BASE_URL}/posts/${payload.id}`,\r\n payload,\r\n );\r\n console.log(\"data\", DataTransfer);\r\n return thunkAPI.fulfillWithValue(data.data);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const deletePosts = createAsyncThunk(\r\n \"DELETE_posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n await axios.delete(`${BASE_URL}/posts/${payload}`);\r\n return thunkAPI.fulfillWithValue(payload);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const postsSlice = createSlice({\r\n name: \"posts\",\r\n initialState,\r\n reducers: {},\r\n extraReducers: {\r\n /* Pending */\r\n [getPosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n [addPosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n [deletePosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n /* Fulfilled */\r\n [getPosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n console.log(action);\r\n state.posts = [...action.payload];\r\n },\r\n [addPosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n state.posts.push(action.payload);\r\n },\r\n [updatePosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n console.log(action);\r\n const newState = state.posts.map(item =>\r\n action.meta.arg.id === item.id\r\n ? { ...item, content: action.meta.arg.content }\r\n : item,\r\n );\r\n state.posts = newState;\r\n return state;\r\n },\r\n [deletePosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n const newState = state.posts.filter(item => item.id !== action.meta.arg);\r\n state.posts = newState;\r\n return state;\r\n },\r\n /* Rejected */\r\n [getPosts.rejected]: (state, action) => {\r\n state.isLoading = false;\r\n state.error = action.payload;\r\n },\r\n },\r\n});\r\n\r\nexport default postsSlice.reducer;\r\n```\r\n"},{"title":"redux typescript react reduxtoolkit","description":"Typescript react에서 ReduxToolkit 사용해보기","category":"redux","date":"2023-03-04","content":"\r\njavascript에서의 ReduxToolkit 사용 예제 : https://lee-yo-han.github.io/redux/redux-toolkit-사용해보기\r\n\r\n## 1. 설치\r\n\r\n```bash\r\nyarn add @reduxjs/toolkit react-redux\r\n```\r\n\r\n## 2. store 설정\r\n\r\nstate와 dispatch type 생성\r\n\r\n```typescript\r\n// store.ts\r\nimport { configureStore } from \"@reduxjs/toolkit\";\r\nimport commentSlice from \"./commentSlice\";\r\n\r\nconst store = configureStore({\r\n reducer: {\r\n commentSlice,\r\n },\r\n});\r\nexport default store;\r\n\r\nexport type RootState = ReturnType; // 1. state type 가져오기\r\nexport type AppDispatch = typeof store.dispatch; // 2. dispatch type 가져오기\r\n```\r\n\r\n## 3. Redux hook 설정\r\n\r\ndispatch 함수와 useSelector 실행을 위해, Redux hook을 따로 설정해준다.\r\n\r\n```typescript\r\n// useRedux.ts\r\n\r\nimport type { TypedUseSelectorHook } from \"react-redux\";\r\nimport type { RootState, AppDispatch } from \"../redux/store\"; // store에서 미리 설정해준 state와 dispatch type\r\n\r\nexport const useAppDispatch: () => AppDispatch = useDispatch;\r\nexport const useAppSelector: TypedUseSelectorHook = useSelector;\r\n```\r\n\r\n위의 설정을 마치면 javascript에서 dispatch를 사용하는 것과 같이 사용할 수 있다.\r\n\r\n```typescript\r\n\r\n// Form.tsx\r\nimport { useAppDispatch, useAppSelector } from \"../hook/useRedux\";\r\nimport { FormEvent } from \"../type\"; // 별도로 지정한 eventType\r\nimport { addComments } from \"../redux/commentSlice\"; // Slice의 action함수\r\n\r\nexport const Form = () => {\r\n\tconst dispatch = useAppDispatch();\r\n\r\n const formData = {...}\r\n\r\n const onSubmitHandler = async (e: FormEvent, dispatch: any) => {\r\n e.preventDefault();\r\n dispatch(addComments(formData)); // 정상작동\r\n };\r\n\r\n return ( ...\r\n}\r\n\r\n```\r\n\r\n참고 : https://redux-toolkit.js.org/usage/usage-with-typescript\r\n"},{"title":"redux usage","description":"redux 사용해보기","category":"redux","date":"2023-03-04","content":"\r\n# React에서 Redux 사용해보기\r\n\r\n리덕스 툴킷 x\r\n\r\n## Redux 설치\r\n\r\n```bash\r\nyarn add redux react-redux\r\n```\r\n\r\n1. src 디렉토리 안에 redux 폴더 생성\r\n2. redux 폴더 안에 config, modules 폴더 생성 (생성할 state들의 그룹)\r\n3. modules 폴더 안 store.js 생성\r\n > 폴더/파일 명은 목적이나 폴더 구조에 맞춰 자유롭게 생성해줘도 괜찮다.\r\n\r\n## 스토어 생성\r\n\r\n```javascript\r\n// src/configStore.js\r\n\r\nimport { createStore, combineReducers } from \"redux\";\r\nimport todo from \"./modules/todo\"; // 모듈에 있는 reducer 불러오기\r\n\r\nconst rootReducer = combineReducers({});\r\nconst store = createStore(rootReducer);\r\n\r\nexport default store;\r\n```\r\n\r\n## 스토어 연동\r\n\r\n```javascript\r\n// 디렉토리 최상단 파일 ex/ index.js or app.js\r\n\r\nimport store from \"./redux/config/configStore\";\r\nimport { Provider } from \"react-redux\";\r\n\r\nconst root = ReactDOM.createRoot(document.getElementById(\"root\"));\r\nroot.render(\r\n //App을 Provider로 감싸주고, configStore에서 export default 한 store를 넣어준다.\r\n \r\n \r\n ,\r\n);\r\nreportWebVitals();\r\n```\r\n\r\n## reducer 편집\r\n\r\n```javascript\r\n// 예제\r\n// todo.js - ./modules에 있는 파일\r\n\r\n// Actions\r\nconst CREATE = \"my-app/todo/CREATE\";\r\nconst REMOVE = \"my-app/todo/REMOVE\";\r\n\r\n// Reducer\r\n//action이 없으면 state는 아래 initialState의 초기값, 있으면 action 값\r\nexport default function reducer(state = initialState, action = {}) {\r\n switch (action.type) {\r\n case CREATE: {\r\n const new_todo_list = [...state.todos, action.todo];\r\n return { todos: new_todo_list };\r\n }\r\n case REMOVE: {\r\n const new_todo_list = action.todo;\r\n return { todos: new_todo_list };\r\n }\r\n\r\n // ...etc\r\n\r\n default:\r\n return state;\r\n }\r\n}\r\n\r\n// Action Creators\r\nexport function createTodo(todo) {\r\n console.log(\"create 액션 생성\");\r\n return { type: CREATE, todo };\r\n}\r\n\r\nconst initialState = {\r\n // createTodo(todo)의 초기값 설정\r\n todos: [\r\n {\r\n id: 1,\r\n name: \"qwert\",\r\n },\r\n {\r\n id: 2,\r\n name: \"asdf\",\r\n },\r\n ],\r\n};\r\n\r\nexport function removeTodo(todo) {\r\n console.log(\"remove 액션 생성\");\r\n return { type: REMOVE, todo };\r\n}\r\n```\r\n\r\n## 컴포넌트에서의 사용\r\n\r\nreducer를 모두 작성해준 후 컴포넌트에서 사용할 수 있다.\r\n\r\n```javascript\r\n\r\n// 리덕스 훅\r\nimport {useSelector , useDispatch} from \"react-redux\";\r\n\r\n\r\nconst test = () =>{\r\n\t// 해당 상수는 useSelector를 통해 reducer에 있는 state 값을 가져온다.\r\n\tconst testList = useSelector((state=>state.todo.todos))\r\n // 액션을 생성하기 위해 준비\r\n const dispatch = useDispatch()\r\n\r\n const addBtn = () =>{\r\n \t// ---실행하고싶은 코드들---\r\n // -------------------------\r\n // dispatch로 액션을 생성했으니, 원하는 처리는 reducer에서 입력하면 된다.\r\n dispatch(createTodo(reducer에서 처리하고 싶은 값))\r\n }\r\n return(\r\n \t
\r\n )\r\n}\r\n\r\n```\r\n\r\n참조 : https://ko.redux.js.org/\r\n"},{"title":"react datepicker","description":"React datepicker 사용하기","category":"react","date":"2023-03-03","content":"\r\n# React Datepicker 사용하기\r\n\r\n공식 문서\r\n\r\nhttps://reactdatepicker.com/\r\n\r\n### package 설치\r\n\r\n```bash\r\nyarn add react-datepicker\r\n```\r\n\r\n패키지 설치 후 아래와 같이 import해 사용할 수 있다.\r\n\r\n```javascript\r\nimport DatePicker from \"react-datepicker\"; // 데이트픽커 import\r\nimport \"react-datepicker/dist/react-datepicker.css\"; // 데이트픽커 기본 CSS\r\nimport { ko } from \"date-fns/esm/locale\"; // 한국어 변환\r\n\r\nexport default function DatePickerTest() {\r\n const [startDate, setStartDate] = useState(new Date());\r\n const [endDate, setEndDate] = useState(null);\r\n\r\n return (\r\n <>\r\n \r\n \r\n );\r\n}\r\n```\r\n\r\n달력은 props 설정을 통해 다양한 방식의 달력을 설정해줄 수 있다.\r\n\r\n아래는 범위 지정 달력을 만드는 코드이다.\r\n\r\n```javascript\r\nexport default function DatePickerTest() {\r\n const [startDate, setStartDate] = useState(new Date());\r\n const [endDate, setEndDate] = useState(null);\r\n\r\n const onChange = dates => {\r\n const [start, end] = dates;\r\n setStartDate(start);\r\n setEndDate(end);\r\n };\r\n\r\n return (\r\n \r\n );\r\n}\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5DkxI%2FbtrMUTz6YZb%2FpYNfcGfYjpM5GZlENLALTk%2Fimg.png)\r\n\r\n위 코드는 selectsRange라는 속성의 달력을 이용한다.\r\n다양한 속성은 상단의 공식 페이지 링크를 참고하면 좋다.\r\n\r\n## 스타일 적용\r\n\r\ncustom header 같은 props 옵션 등을 통해 css 스타일을 줄 수 있지만, 스타일이 원하는대로 입히기 어려운 점도 있었고 전체적인 코드가 보기 불편하다는 생각이 들어 styled component를 통해 css를 적용해줬다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzDzqF%2FbtrMWeKqb6T%2FkKiJfjfF1fP7vPcnWpyHOK%2Fimg.png)\r\n\r\n```javascript\r\n\r\nconst StyledDatePickerWrapper = styled.section`\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n width: 300px;\r\n height: 380px;\r\n border-radius: 20px;\r\n box-shadow: 0 0 5px 0 rgb(71, 181, 255);\r\n\r\n .react-datepicker {\r\n background-color: white;\r\n border-color: rgb(198, 232, 255);\r\n border-radius: 20px;\r\n }\r\n\r\n /* ... */\r\n\r\n```\r\n\r\n코드가 길어지더라도 jsx와 철저하게 분리하고 싶다면 이런 방법도 나쁘지 않은 것 같다.\r\n"},{"title":"react media query","description":"React mediaQuery 사용법","category":"react","date":"2023-03-03","content":"\r\n# react-responsive 사용해보기\r\n\r\n공식 사이트 : https://yarnpkg.com/package/react-responsive\r\n\r\nCSS의 mediaQuery를 사용해볼 수도 있지만, 간편하게 적용할 수 있는 패키지가 있어서 사용해봤다.\r\n\r\n### 패키지 설치\r\n\r\n```bash\r\nyarn add react-responsive\r\n```\r\n\r\n### useMediaQuery 세팅\r\n\r\n```javascript\r\n// useMideaQuery.js\r\n\r\nimport { useMediaQuery } from \"react-responsive\";\r\n\r\nexport const Desktop = ({ children }) => {\r\n const isDesktop = useMediaQuery({\r\n minWidth: 481,\r\n });\r\n return isDesktop ? children : null;\r\n};\r\n\r\nexport const Mobile = ({ children }) => {\r\n const isMobile = useMediaQuery({ maxWidth: 480 });\r\n return isMobile ? children : null;\r\n};\r\n```\r\n\r\n위의 경우 모바일과 PC만 나눴지만 대략적인 분기점은 아래와 같이 설정해도 괜찮다\r\n\r\n1. 낮은 해상도의 PC, 태블릿 가로 : ~1024px\r\n2. 태블릿 가로 : 768px ~ 1023px\r\n3. 모바일 가로, 태블릿 : 480px ~ 767px\r\n4. 모바일 : ~480\r\n\r\n### useMediaQuery 사용\r\n\r\n```javascript\r\n// Responsive.jsx\r\n\r\nimport { Desktop, Mobile } from \"../../hooks/useMideaQuery\";\r\n\r\nexport const Responsive = () => {\r\n return (\r\n <>\r\n \r\n
PC화면
\r\n
\r\n \r\n
모바일화면
\r\n
\r\n \r\n );\r\n};\r\n```\r\n\r\n편의상 한 컴포넌트에 다 집어넣을 수도 있지만, 모바일과 데스크탑 환경의 view는 아예 다른 경우가 많기 때문에, 코드 관리를 편하게 하기 위해서는 모바일 컴포넌트를 따로 만드는게 더 나을 것 같다는 생각이 든다.\r\n"},{"title":"react modal non library","description":"라이브러리 없이 React Modal 만들기","category":"react","date":"2023-03-03","content":"\r\n# 라이브러리 없이 모달창 만들어보기\r\n\r\n모달 버튼이 있는 페이지\r\n\r\n```javascript\r\n// ModalTest.jsx\r\n\r\nimport ModalPage from \"./ModalPage\";\r\nimport { useState } from \"react\";\r\n\r\nexport default function ModalTest() {\r\n // 모달을 보여줄지 말지 상태를 관리하는 state를 만들어준다.\r\n const [showModal, setShowModal] = useState(false);\r\n\r\n // 모달 버튼을 클릭하면 열리고\r\n const openModal = () => {\r\n setShowModal(true);\r\n };\r\n // 활성화된 모달창 밖을 클릭하면 닫힌다.\r\n const closeModal = () => {\r\n setShowModal(false);\r\n };\r\n\r\n return (\r\n <>\r\n \r\n // 모달상태가 true면 ModalPage를 보여주고, 아니면 null // props로 showModal과\r\n closeModal을 전달한다.\r\n {showModal === true ? (\r\n \r\n ) : null}\r\n \r\n );\r\n}\r\n```\r\n\r\n모달 컴포넌트\r\n\r\n```javascript\r\nimport styled from \"styled-components\";\r\n\r\nexport default function ModalPage({ showModal, closeModal }) {\r\n return (\r\n // 모달 밖을 클릭하면 모달창을 닫게 만든다.\r\n \r\n // stopPropagation은 부모태그로부터의 이벤트 전파를 중지시킨다. // 이\r\n 친구가 없으면 모달창 안쪽을 클릭해도 closeModal이 실행된다.\r\n e.stopPropagation()}>\r\n 열린 모달창이에옹\r\n \r\n \r\n );\r\n}\r\n\r\n// 위치를 대략 가운대로 정해주고\r\nconst StyledModalContainer = styled.div`\r\n position: fixed;\r\n left: 50%;\r\n top: 50%;\r\n transform: translate(-50%, -50%);\r\n`;\r\n\r\n// 모달 밖 배경색은 우리에게 익숙한 어두운 색으로 만들어주자\r\nconst StyledModalBackground = styled.div`\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n bottom: 0;\r\n right: 0;\r\n background-color: rgba(0, 0, 0, 0.4);\r\n z-index: 0;\r\n cursor: auto;\r\n`;\r\n\r\n// 모달창 사이즈는 용도에 맞춰 설정해준다.\r\nconst StyledModal = styled.div`\r\n width: 400px;\r\n height: 400px;\r\n background-color: white;\r\n`;\r\n```\r\n\r\n브라우저 화면\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOLJGf%2FbtrNjUdVndT%2FvgVskBO2xWeRwi01YQPzkk%2Fimg.png)\r\n\r\n스타일은 용도에 맞게 다양하게 변경시킬 수 있다.\r\n"},{"title":"react navigate props","description":"React navigate로 props 넘기기","category":"react","date":"2023-03-03","content":"\r\n# useNavigate를 이용한 props 전달\r\n\r\n### useNavigate로 다른 페이지의 화면으로 이동할 때, props처럼 값을 넘기는 방법을 사용해볼 수 있다.\r\n\r\n```javascript\r\n\r\n// 보내는컴포넌트.jsx\r\n\r\nimport { useNavigate } from \"react-router-dom\";\r\n\r\nconst Component1 = () => {\r\nconst navigate = useNavigate()\r\n\r\nlet someDatas = {\r\n\tname:\"hihi\"\r\n value:\"here\"\r\n}\r\n\r\nreturn\r\n
navigate(`/다른페이지주소`,{ state: someDatas });}>\r\n\t다른페이지로 이동\r\n
\r\n}\r\n\r\n\r\n\r\n// 받는 컴포넌트.jsx\r\n\r\nimport { useLocation } from \"react-router-dom\";\r\n\r\nconst Component2 = () => {\r\n\tconst location = useLocation();\r\n\tconsole.log(location);\r\n}\r\n\r\n```\r\n\r\n위와 같이 작성은 아래와 같이 데이터를 받아오는 것을 보여준다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedpbW9%2FbtrRGwsRGuz%2FKp5GIecYeABXw1FPbGLdsK%2Fimg.png)\r\n\r\n하지만 데이터를 받는 페이지가 직접적으로 접속할 수 있는 페이지라면 그렇게 접속한 페이지는 아무 데이터가 없는 화면으로 노출될 수 있기 때문에, 전역 상태를 관리해줄지, 데이터를 넘겨줄지 상황에 알맞는 방식을 적용하는 것이 중요하다.\r\n"},{"title":"react simple formdata code","description":"react formdata 코드간소화","category":"react","date":"2023-03-03","content":"\r\n# FormData 저장 코드 간소화하기\r\n\r\n하나의 폼에서 여러 개의 input 값을 변경하기 위해 함수를 만들어 간단하게 코드를 간소화시킬 수 있다.\r\n\r\n```typescript\r\n// form.tsx\r\n onSubmitHandler(e, dispatch)}>\r\n onChangeHandler(e)}\r\n />\r\n
\r\n onChangeHandler(e)}\r\n required\r\n />\r\n
\r\n onChangeHandler(e)}\r\n required\r\n >\r\n
\r\n \r\n
\r\n \r\n
;\r\n\r\n// useForm.tsx\r\nconst [formData, setFormData] = useState({\r\n profile_url: \"\",\r\n author: \"\",\r\n content: \"\",\r\n createdAt: todayDate(),\r\n});\r\n\r\nconst onChangeHandler = (e: InputEvent) => {\r\n let type = e.target.name;\r\n let value = e.target.value;\r\n if (type === \"profile_url\")\r\n setFormData(prev => ({ ...prev, profile_url: value }));\r\n if (type === \"author\") setFormData(prev => ({ ...prev, author: value }));\r\n if (type === \"content\") setFormData(prev => ({ ...prev, content: value }));\r\n setFormData(prev => ({ ...prev, [type]: value }));\r\n};\r\n```\r\n\r\n하지만 위와 같이 if문이 많아져 코드의 성능과 가독성을 떨어뜨릴 수 있기 때문에, 아래와 같이 변경해볼 수 있다.\r\n\r\n```typescript\r\n// useForm.tsx\r\n\r\nconst [formData, setFormData] = useState({\r\n profile_url: \"\",\r\n author: \"\",\r\n content: \"\",\r\n createdAt: todayDate(),\r\n});\r\n\r\nconst onChangeHandler = (e: InputEvent) => {\r\n const { name, value } = e.target.name;\r\n setFormData(prev => ({ ...prev, [name]: value }));\r\n};\r\n```\r\n\r\nhtml input의 name 속성을 이용해 setState 코드를 간소화시킬 수 있는 방법이다.\r\n"},{"title":"react styled components hover","description":"react에 styled-components를 이용해 hover 적용하기","category":"react","date":"2023-03-03","content":"\r\n# 리액트 스타일 컴포넌트에 hover 적용하기\r\n\r\n```typescript\r\n// &:hover를 넣는다.\r\n\r\nconst ButtonStyle = styled.button`\r\n &:hover {\r\n background-color: skyblue;\r\n color: blue;\r\n }\r\n`;\r\n```\r\n\r\n비슷한 상황에서 a 태그를 사용할때 자동으로 설정되는 스타일도 수정해줄 수 있다.\r\n\r\n```typescript\r\nconst LinkTag = styled.a`\r\n /* 밑줄 제거 */\r\n text-decoration: none;\r\n\r\n /* 마우스를 링크에 올려뒀을 때의 스타일 */\r\n &:hover {\r\n }\r\n /* 아직 방문하지 않은 링크의 스타일 */\r\n &:link {\r\n }\r\n /* 사용자가 방문한 적이 있는(클릭한 이후) 링크의 스타일 */\r\n &:visited {\r\n }\r\n /* 마우스로 링크를 클릭하고 뗄 때까지의 스타일 */\r\n &:active {\r\n }\r\n`;\r\n```\r\n"},{"title":"react submit prevent default","description":"form 태그에서 submit 이벤트 방지하기","category":"react","date":"2023-03-03","content":"\r\n# react form에서 submit 이벤트 방지하기\r\n\r\n### submit 이벤트가 발생하면 페이지가 새로고침이 된다.\r\n\r\n단순히 페이지가 리프레시 되는 것도 좋은 사용자 경험이 아닌데, 임시로 저장돼있던 데이터(회원가입 정보 등)가 날아가면 React의 작동 방식을 거스를 뿐 아니라 사용자 경험에도 치명적이다.\r\n\r\n떄문에 우리는 sumbit 이벤트를 멈춰줄 필요가 있다.\r\n\r\n```javascript\r\nconst Header = () => {\r\n const onSubmit = e => {\r\n e.preventDefault(); // 해당 코드로 이벤트를 멈춰줄 수 있다.\r\n };\r\n\r\n return (\r\n
\r\n \r\n // 버튼 타입도 submit으로 꼭 변경해준다.\r\n \r\n
\r\n );\r\n};\r\n```\r\n\r\n위와 같이 작성하면 같은 폼 안에 있는 경우에 대해 submit 방지가 잘 되는 것을 확인할 수 있다.\r\n"},{"title":"react z index error","description":"z-index가 올바르게 적용되지 않을때 적용해볼 수 있는 방법","category":"react","date":"2023-03-03","content":"\r\n# z-index 미적용시 해결방법\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2c0QV%2FbtrXD8G7Ar9%2FpCFQ6eujsGgA0bKCKx1kpk%2Fimg.png)\r\n\r\n위와 같이 z-index를 999로 적용해도 원하는대로 작동하지 않는 경우를 볼 수 있다.\r\n\r\n### MDN 공식 문서 : https://developer.mozilla.org/ko/docs/Web/CSS/z-index\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8pPBj%2FbtrXCijsq0h%2FkdCdCRUKvZIGe7dk8KPJf0%2Fimg.png)\r\n\r\n해당 사진의 마지막 줄의 \"자손의 z-index를 자기 외의 바깥 요소와 비교하지 않습니다.\" 이 부분으로 인한 문제로 생각됐다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fct1Z18%2FbtrXH8zek29%2Fjwx0ckk9YgnPRLS2sM06k0%2Fimg.png)\r\n\r\n부모 요소는 부모 요소끼리, 자식 요소는 자식 요소끼리 경쟁하기 때문에, 기존 코드는 이와 같이 HeaderNav 안에 SideMenu가 있어 HeaderNav 내에서만 z-index를 비교하고 있던 것이었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUIap4%2FbtrXCicOSRi%2FzWiWf6wrtlUx9aK2uYKK7K%2Fimg.png)\r\n\r\n이처럼 SideMenu를 HeaderNav 밖으로 빼주니, 아래와 같이 정상적으로 작동하는 모습을 볼 수 있었다.\r\n\r\n### 정상 적용된 화면\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwMc3x%2FbtrXFupLVFz%2FWH6kLZ1IbRxHnXK2bqkHa0%2Fimg.png)\r\n\r\n## z-index가 제대로 작동하지 않을때 참고할만한 사항\r\n\r\n1. 부모 요소는 부모 요소끼리 경쟁되고 있는지 확인한다.\r\n2. Element가 static(position 속성의 default 값)이 아닌 position 속성이 설정되어있는지 확인한다.(relative, absolute, fixed, sticky)\r\n3. opacity나 transform과 같은 css 속성이 설정돼있는지 확인한다. 해당 css요소가 설정돼있다면 1번의 기준에 맞춰 코드를 다시 작성해준다.\r\n\r\n이정도면 버그를 수정하는데 충분하지만 더 싶도 깊은 내용은 아래를 참고해보면 좋을 것 같다.\r\n\r\n참조 : https://coder-coder.com/z-index-isnt-working/\r\n"},{"title":"typescript syntax","description":"Typescript 사용을 위한 기본적인 문법 정리","category":"typescript","date":"2023-03-02","content":"\r\n# 기본 TypeScript 타입 선언\r\n\r\n```typescript\r\n\r\n// 문자열\r\nlet str: string = \"hello\";\r\n\r\n// 숫자\r\nlet num: number = 100;\r\n\r\n// 배열\r\nlet arr: Array = [10,20,30];\r\nlet arr2: number[] = [10,20,30];\r\nlet arr3: Array = [\"hello\",\"hellololo\" ];\r\nlet arr4: [string, number] = [\"hello\", 182];\r\n\r\n// 객체\r\nlet obj:object = {name:\"hello\", age:29};\r\nlet person:{name: string, age:number};\r\n\r\n// Boolean\r\nlet isAvaliable: boolean = true;\r\n\r\n\r\n// 함수 선언\r\nparameter와 return 값에 대해 타입 선언 가능\r\nconst sum = (a:number, b:number):number => {\r\nreturn a+b;\r\n}\r\n\r\n// optional parameter일 경우 ?를 사용\r\n\r\nconst log = (time: string, result?: string, option?: string) => {\r\nconsole.log(time, result, option);\r\n}\r\nlog(\"2021-10-04\", \"success\");\r\n\r\n```\r\n\r\n# 인터페이스 (interface)\r\n\r\n### 자주 사용하는 타입들을 object 형태의 묶음으로 정의해 새로운 타입을 만들어 사용하는 기능\r\n\r\n```typescript\r\n\r\n// interface 선언\r\ninterface User {\r\nage: number;\r\nname: string;\r\n}\r\n\r\n// 변수 활용\r\nconst hanbbi: User = { age: 30, name: \"hello\"}\r\n\r\n\r\n// 함수 인자로의 활용\r\nconst getUser = (user:User){\r\nconsole.log(user);\r\n}\r\ngetUser({ age:10, name: \"hanbbi\" })\r\n\r\n\r\n\r\n// 함수 구조 활용\r\ninterface Sum {\r\n(a:number, b:number): number;\r\n}\r\n\r\nlet sumFinc: Sum:\r\nsumFunc = function(a: number, b: number): number {\r\nreturn a+b;\r\n}\r\n\r\n\r\n\r\n// 배열 활용\r\ninterface StringArray {\r\n[index:number]: string;\r\n}\r\n\r\nlet arr: StringArray = [\"a\", \"b\", \"c\"];\r\n\r\n\r\n// 객체 활용\r\ninterface StringRegexObject {\r\n[key: string]: RegExp;\r\n}\r\n\r\nconst obj: StringRegexObject {\r\ncssFile: /\\.css$/,\r\n jsFile: /\\.js$/\r\n}\r\n\r\n\r\n// interface 확장 (extends 사용)\r\ninterface Person {\r\nname: string;\r\nage:number;\r\n}\r\ninterface Developer extends Person {\r\nskill: string;\r\n}\r\n\r\nconst juniorDeveloper = {\r\nname: \"hanbbi\",\r\nage:100,\r\nskill: \"JS\"\r\n}\r\n\r\n```\r\n\r\n# 타입 별칭(type aliases)\r\n\r\n### 타입 키워드는 interface와 다르게 새로운 타입을 생성하는 것이 아닌 별칭을 부여하는 것으로, extends 키워드는 사용할 수 없음\r\n\r\n```typescript\r\n\r\n// 타입 별칭 선언 및 활용\r\ntype MyString = string;\r\nconst str: MyString = \"Hello dear\"\r\n\r\ntype Todo = {\r\nid: string;\r\ntitle: string;\r\ndone: boolean\r\n}\r\n\r\nconst getTodo(todo:Todo){\r\nconsole.log(todo);\r\n}\r\n\r\n```\r\n\r\n# 연산자 (Operator)\r\n\r\n## Uinon Type\r\n\r\n### 한 가지 이상의 type을 선언하고자 할 때 사용 가능. | 기호 사용\r\n\r\n```typescript\r\nconst logMessage = (value: string | number) => {\r\n if (typeof value === \"string\") {\r\n value.toString();\r\n } else if (typeof value === \"number\") {\r\n value.toLocaleString();\r\n } else {\r\n throw new TypeError(\"value must be string or number\");\r\n }\r\n};\r\nlogMessage(\"hello\");\r\nlogMessage(1000);\r\n```\r\n\r\n## intersection Type\r\n\r\n### 합집합과 같은 개념으로, 함수 호출의 경우 함수 인자에 명시한 type을 모두 제공해야 한다. & 기호 사용\r\n\r\n```typescript\r\n\r\ninterface Zoo {\r\nname: string;\r\nlocation: string\r\nprice: number;\r\n}\r\n\r\ninterface Animal {\r\nname: string;\r\ncount: number;\r\n}\r\n\r\nconst askZookeeper = ( value : Zoo & Animal ) => {\r\n // value 는\r\n { name:\"어린이대공원\", location: \"서울시 광진구\", price: 10000, count: 10000}\r\n // 와 같이 Zoo와 Animal이 모두 포함되는 인자를 줘야한다.\r\n}\r\n\r\n```\r\n\r\n# Enum\r\n\r\n### enum 키워드를 사용하면 일종의 default 값을 선언할 수 있다.\r\n\r\n```typescript\r\n\r\n// 숫자형 enum\r\n// 자동으로 0에서 1씩 증가하는 값을 부여\r\n\r\nenum Shoes {\r\nNike, // 0\r\nAdidas, // 1\r\nNewBalance //2\r\n}\r\nconst myShoes = Shoes.Nike; // 0\r\n\r\n문자형 enum\r\nenum Food {\r\ncake = \"케익\",\r\ncookie = \"쿠키\"\r\n}\r\nconst player = Food.cookie; // 쿠키\r\n\r\n```\r\n\r\n# 제네릭\r\n\r\n### 제네릭을 활용하면 인자를 넘겨 호출하는 시점에 타입을 결정할 수 있다. 제네릭 활용 시 동일한 기능을 하는 함수를 일일이 만들 필요가 없으며, 타입 추론에 있어 강점을 가진다.\r\n\r\n제네릭 선언\r\n와 같이 타입을 선언한다. 알파벳은 통상 T로 정해져 있다.\r\n\r\n```typescript\r\n\r\nconst logText = (text: T):T => {\r\nconsole.log(text);\r\nreturn text;\r\n}\r\nlogText(\"Hello hanbbi\");\r\n\r\n\r\n// interface에 제네릭 선언\r\n\r\ninterface Dropdown {\r\nvalue: T;\r\nselected: boolean;\r\n}\r\ncosnt obj: Dropdown = { value: \"hamburger\" , selected: true};\r\n\r\n```\r\n\r\n# 제네릭 타입 제한\r\n\r\n## 1. 배열 힌트\r\n\r\n```typescript\r\n\r\nconst logTextLength = (text: T[]): T[] =>{\r\nconsole.log(text.length);\r\ntext.forEach(text =>{\r\nconsole.log(text):\r\n});\r\n}\r\nlogTextLength([\"hi\", \"hello\"]);\r\n\r\n\r\n```\r\n\r\n## 2. 정의된 타입 이용(extends)과 keyof\r\n\r\n```typescript\r\n\r\ninterface ShoppingItem {\r\nname: string;\r\nprice: number;\r\nstock: number;\r\n}\r\n\r\nconst getShoppingItemOption(itemOption: T): T {\r\nreturn itemOption;\r\n}\r\n\r\n// \"name\", \"price\", \"stock\"만 인자로 가능\r\ngetShoppingItemOption(\"price\");\r\n\r\n```\r\n\r\n# 타입 추론 (Type inference)\r\n\r\n## 1. 기본 변수 타입 추론\r\n\r\n```typescript\r\n\r\n// string으로 추론\r\nlet a = \"abc\";\r\n\r\n// a: number로 추론\r\n// b: string으로 추론\r\n// return value는 string으로 추론\r\nconst getValue(a = 10) {\r\nlet b = \"hello\";\r\nreturn a + b;\r\n}\r\n\r\n\r\n```\r\n\r\n## 2. interface추론\r\n\r\n```typescript\r\n\r\n// interface 1개\r\ninterface Dropdown {\r\nvalue: T;\r\ntitle: string;\r\n}\r\nconst shoppingItem:Dropdown ={\r\nvalue: 10000;\r\ntitle: \"shoe\"\r\n}\r\n// interface 2개\r\ninterface Dropdown2 {\r\nvalue: T;\r\ntitle: string;\r\n}\r\ninterface DetailedDropdown extends Dropdown2{\r\ntag: K;\r\ndesc: string;\r\n}\r\nconst detailed: DetailedDropdown{\r\nvalue: \"10000\";\r\ntitle: \"shoe\",\r\ntag: \"10000\",\r\ndesc: \"description\"\r\n}\r\n\r\n```\r\n\r\n# 타입 단언 (Type assertion)\r\n\r\n### as 키워드를 사용해 타입을 정함으로써 typescript에게 타입을 알려줄 수 있다. 주로 DOM API를 조작할 떄 사용한다.\r\n\r\n```typescript\r\n// div가 있는지 장담할 수 없음, HTMLDivElement | null\r\n// 따라서 typescript에게 타입을 단언해 타입을 알려줄 수 있음.\r\nconst div = document.querySelector(\"div\") as HTMLDivElement;\r\ndiv.innerText = \"test\";\r\n```\r\n\r\n# 타입 가드 (Type guard)\r\n\r\n### union type을 사용하는 경우 공통된 속성만 접근 가능하며, 로직상 공통되지 않은 속성에 접근하고자 할 때 불편함을 해소하기 위해 타입 단언으로 공통되지 않은 속성에 접근하고자 하는 방법이 있지만, 코드 가독성을 위해 타입 가드 방법을 주로 사용한다.\r\n\r\n```typescript\r\nconst isDeveloper = (target: Developer | Humanoid): target is Developer => {\r\n return (target as Developer).skill !== undefined;\r\n};\r\nif (isDeveloper(tom)) {\r\n console.log(tom.name);\r\n console.log(tom.skill);\r\n} else {\r\n console.log(tom.name);\r\n console.log(tom.age);\r\n}\r\n```\r\n\r\n# 타입 호환 (Type compatibility)\r\n\r\n### TypeScript에서 더 큰 타입 구조를 갖는 변수에 작은 타입 구조를 갖는 변수를 할당 가능\r\n\r\n```typescript\r\nlet add = function (a: number) {\r\n // ...\r\n};\r\nlet sum = function (a: number, b: number) {\r\n // ...\r\n};\r\n// 아래는 에러가\r\n// add = sum;\r\n\r\n// 에러가 나지 않는 방식. sum의 구조가 더 크다고 볼 수 있음\r\nsum = add;\r\n```\r\n\r\n참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/\r\n"},{"title":"html input basic","description":"HTML input 속성 이용하기","category":"html","date":"2023-02-09","content":"\r\n# Input element 사용하기\r\n\r\n## 타입\r\n\r\n- type=\"text\" 일반 텍스트\r\n- type=\"password\" 패스워드 형식(마스킹 처리)\r\n- type=\"eamil\" 이메일 형식\r\n- type=\"radio\" 설문 등 여러 선택지 중 하나의 값을 선택하는 형식\r\n- type=\"checkbox\" 설문 등 여러 선택지 중 여러 값을 선택하는 형식\r\n- type=\"number\" 숫자\r\n- type=\"file\" 파일 업로드\r\n- type=\"date\" 달력\r\n- type=\"range\" 볼륨 조절 위젯같이 값이 가려진 숫자를 입력하는 조작 가능\r\n\r\n## 속성\r\n\r\n- accept=\"업로드할 파일의 기댓값 암시\"\r\n- placeholder=\"input 안 힌트 텍스트\"\r\n- autofocus \"페이지 전환 시 자동으로 맨 처음 포커싱\"\r\n- required \"필수값\"\r\n- maxlength=\"최댓값\"\r\n- disabled \"비활성화\"\r\n- autocomplete=\"off\" \"브라우저의 자동완성 제거\"\r\n- checked \"radio나 checkbox 등의 체크 여부\"\r\n- name input의 이름으로 radio나 checkbox의 카테고리 묶음이나 javascript 이벤트 등을 유연하게 적용할 수 있음\r\n"}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/css-a-tag-style-fix.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/css-a-tag-style-fix.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/css-a-tag-style-fix.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/css-a-tag-style-fix.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/css-no-selection.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/css-no-selection.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/css-no-selection.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/css-no-selection.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/css-use-download-font.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/css-use-download-font.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/css-use-download-font.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/css-use-download-font.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/css.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/css.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/css.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/css.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/github-git-blog-utterances-comment.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-git-blog-utterances-comment.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/github-git-blog-utterances-comment.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-git-blog-utterances-comment.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/github-git-cmd.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-git-cmd.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/github-git-cmd.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-git-cmd.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/github-git-custom-blog.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-git-custom-blog.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/github-git-custom-blog.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-git-custom-blog.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/github-git-use-markdown-viewer.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-git-use-markdown-viewer.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/github-git-use-markdown-viewer.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-git-use-markdown-viewer.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/github-github-api-get-repo-data.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-github-api-get-repo-data.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/github-github-api-get-repo-data.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-github-api-get-repo-data.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/github-markdown-syntex.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-markdown-syntex.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/github-markdown-syntex.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-markdown-syntex.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/github-token-expired.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-token-expired.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/github-token-expired.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github-token-expired.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/github.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/github.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/github.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/html-input-basic.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/html-input-basic.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/html-input-basic.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/html-input-basic.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/html-meta-tag.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/html-meta-tag.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/html-meta-tag.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/html-meta-tag.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/html.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/html.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/html.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/html.json diff --git a/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/index.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/index.json new file mode 100644 index 00000000..f7fdd323 --- /dev/null +++ b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/index.json @@ -0,0 +1 @@ +{"pageProps":{"posts":[{"title":"sw test","description":"정보처리기사 테스트","category":"정보처리기사","date":"2023-12-27","content":"\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n```typescript\r\n\r\n// 문자열\r\nlet str: string = \"hello\";\r\n\r\n// 숫자\r\nlet num: number = 100;\r\n\r\n// 배열\r\nlet arr: Array = [10,20,30];\r\nlet arr2: number[] = [10,20,30];\r\nlet arr3: Array = [\"hello\",\"hellololo\" ];\r\nlet arr4: [string, number] = [\"hello\", 182];\r\n\r\n// 객체\r\nlet obj:object = {name:\"hello\", age:29};\r\nlet person:{name: string, age:number};\r\n\r\n// Boolean\r\nlet isAvaliable: boolean = true;\r\n\r\n\r\n// 함수 선언\r\nparameter와 return 값에 대해 타입 선언 가능\r\nconst sum = (a:number, b:number):number => {\r\nreturn a+b;\r\n}\r\n\r\n// optional parameter일 경우 ?를 사용\r\n\r\nconst log = (time: string, result?: string, option?: string) => {\r\nconsole.log(time, result, option);\r\n}\r\nlog(\"2021-10-04\", \"success\");\r\n\r\n```\r\n\r\n# 인터페이스 (interface)\r\n\r\n### 자주 사용하는 타입들을 object 형태의 묶음으로 정의해 새로운 타입을 만들어 사용하는 기능\r\n\r\n```typescript\r\n\r\n// interface 선언\r\ninterface User {\r\nage: number;\r\nname: string;\r\n}\r\n\r\n// 변수 활용\r\nconst hanbbi: User = { age: 30, name: \"hello\"}\r\n\r\n\r\n// 함수 인자로의 활용\r\nconst getUser = (user:User){\r\nconsole.log(user);\r\n}\r\ngetUser({ age:10, name: \"hanbbi\" })\r\n\r\n\r\n\r\n// 함수 구조 활용\r\ninterface Sum {\r\n(a:number, b:number): number;\r\n}\r\n\r\nlet sumFinc: Sum:\r\nsumFunc = function(a: number, b: number): number {\r\nreturn a+b;\r\n}\r\n\r\n\r\n\r\n// 배열 활용\r\ninterface StringArray {\r\n[index:number]: string;\r\n}\r\n\r\nlet arr: StringArray = [\"a\", \"b\", \"c\"];\r\n\r\n\r\n// 객체 활용\r\ninterface StringRegexObject {\r\n[key: string]: RegExp;\r\n}\r\n\r\nconst obj: StringRegexObject {\r\ncssFile: /\\.css$/,\r\n jsFile: /\\.js$/\r\n}\r\n\r\n\r\n// interface 확장 (extends 사용)\r\ninterface Person {\r\nname: string;\r\nage:number;\r\n}\r\ninterface Developer extends Person {\r\nskill: string;\r\n}\r\n\r\nconst juniorDeveloper = {\r\nname: \"hanbbi\",\r\nage:100,\r\nskill: \"JS\"\r\n}\r\n\r\n```\r\n\r\n# 타입 별칭(type aliases)\r\n\r\n### 타입 키워드는 interface와 다르게 새로운 타입을 생성하는 것이 아닌 별칭을 부여하는 것으로, extends 키워드는 사용할 수 없음\r\n\r\n```typescript\r\n\r\n// 타입 별칭 선언 및 활용\r\ntype MyString = string;\r\nconst str: MyString = \"Hello dear\"\r\n\r\ntype Todo = {\r\nid: string;\r\ntitle: string;\r\ndone: boolean\r\n}\r\n\r\nconst getTodo(todo:Todo){\r\nconsole.log(todo);\r\n}\r\n\r\n```\r\n\r\n# 연산자 (Operator)\r\n\r\n## Uinon Type\r\n\r\n### 한 가지 이상의 type을 선언하고자 할 때 사용 가능. | 기호 사용\r\n\r\n```typescript\r\nconst logMessage = (value: string | number) => {\r\n if (typeof value === \"string\") {\r\n value.toString();\r\n } else if (typeof value === \"number\") {\r\n value.toLocaleString();\r\n } else {\r\n throw new TypeError(\"value must be string or number\");\r\n }\r\n};\r\nlogMessage(\"hello\");\r\nlogMessage(1000);\r\n```\r\n\r\n## intersection Type\r\n\r\n### 합집합과 같은 개념으로, 함수 호출의 경우 함수 인자에 명시한 type을 모두 제공해야 한다. & 기호 사용\r\n\r\n```typescript\r\n\r\ninterface Zoo {\r\nname: string;\r\nlocation: string\r\nprice: number;\r\n}\r\n\r\ninterface Animal {\r\nname: string;\r\ncount: number;\r\n}\r\n\r\nconst askZookeeper = ( value : Zoo & Animal ) => {\r\n // value 는\r\n { name:\"어린이대공원\", location: \"서울시 광진구\", price: 10000, count: 10000}\r\n // 와 같이 Zoo와 Animal이 모두 포함되는 인자를 줘야한다.\r\n}\r\n\r\n```\r\n\r\n# Enum\r\n\r\n### enum 키워드를 사용하면 일종의 default 값을 선언할 수 있다.\r\n\r\n```typescript\r\n\r\n// 숫자형 enum\r\n// 자동으로 0에서 1씩 증가하는 값을 부여\r\n\r\nenum Shoes {\r\nNike, // 0\r\nAdidas, // 1\r\nNewBalance //2\r\n}\r\nconst myShoes = Shoes.Nike; // 0\r\n\r\n문자형 enum\r\nenum Food {\r\ncake = \"케익\",\r\ncookie = \"쿠키\"\r\n}\r\nconst player = Food.cookie; // 쿠키\r\n\r\n```\r\n\r\n# 제네릭\r\n\r\n### 제네릭을 활용하면 인자를 넘겨 호출하는 시점에 타입을 결정할 수 있다. 제네릭 활용 시 동일한 기능을 하는 함수를 일일이 만들 필요가 없으며, 타입 추론에 있어 강점을 가진다.\r\n\r\n제네릭 선언\r\n와 같이 타입을 선언한다. 알파벳은 통상 T로 정해져 있다.\r\n\r\n```typescript\r\n\r\nconst logText = (text: T):T => {\r\nconsole.log(text);\r\nreturn text;\r\n}\r\nlogText(\"Hello hanbbi\");\r\n\r\n\r\n// interface에 제네릭 선언\r\n\r\ninterface Dropdown {\r\nvalue: T;\r\nselected: boolean;\r\n}\r\ncosnt obj: Dropdown = { value: \"hamburger\" , selected: true};\r\n\r\n```\r\n\r\n# 제네릭 타입 제한\r\n\r\n## 1. 배열 힌트\r\n\r\n```typescript\r\n\r\nconst logTextLength = (text: T[]): T[] =>{\r\nconsole.log(text.length);\r\ntext.forEach(text =>{\r\nconsole.log(text):\r\n});\r\n}\r\nlogTextLength([\"hi\", \"hello\"]);\r\n\r\n\r\n```\r\n\r\n## 2. 정의된 타입 이용(extends)과 keyof\r\n\r\n```typescript\r\n\r\ninterface ShoppingItem {\r\nname: string;\r\nprice: number;\r\nstock: number;\r\n}\r\n\r\nconst getShoppingItemOption(itemOption: T): T {\r\nreturn itemOption;\r\n}\r\n\r\n// \"name\", \"price\", \"stock\"만 인자로 가능\r\ngetShoppingItemOption(\"price\");\r\n\r\n```\r\n\r\n# 타입 추론 (Type inference)\r\n\r\n## 1. 기본 변수 타입 추론\r\n\r\n```typescript\r\n\r\n// string으로 추론\r\nlet a = \"abc\";\r\n\r\n// a: number로 추론\r\n// b: string으로 추론\r\n// return value는 string으로 추론\r\nconst getValue(a = 10) {\r\nlet b = \"hello\";\r\nreturn a + b;\r\n}\r\n\r\n\r\n```\r\n\r\n## 2. interface추론\r\n\r\n```typescript\r\n\r\n// interface 1개\r\ninterface Dropdown {\r\nvalue: T;\r\ntitle: string;\r\n}\r\nconst shoppingItem:Dropdown ={\r\nvalue: 10000;\r\ntitle: \"shoe\"\r\n}\r\n// interface 2개\r\ninterface Dropdown2 {\r\nvalue: T;\r\ntitle: string;\r\n}\r\ninterface DetailedDropdown extends Dropdown2{\r\ntag: K;\r\ndesc: string;\r\n}\r\nconst detailed: DetailedDropdown{\r\nvalue: \"10000\";\r\ntitle: \"shoe\",\r\ntag: \"10000\",\r\ndesc: \"description\"\r\n}\r\n\r\n```\r\n\r\n# 타입 단언 (Type assertion)\r\n\r\n### as 키워드를 사용해 타입을 정함으로써 typescript에게 타입을 알려줄 수 있다. 주로 DOM API를 조작할 떄 사용한다.\r\n\r\n```typescript\r\n// div가 있는지 장담할 수 없음, HTMLDivElement | null\r\n// 따라서 typescript에게 타입을 단언해 타입을 알려줄 수 있음.\r\nconst div = document.querySelector(\"div\") as HTMLDivElement;\r\ndiv.innerText = \"test\";\r\n```\r\n\r\n# 타입 가드 (Type guard)\r\n\r\n### union type을 사용하는 경우 공통된 속성만 접근 가능하며, 로직상 공통되지 않은 속성에 접근하고자 할 때 불편함을 해소하기 위해 타입 단언으로 공통되지 않은 속성에 접근하고자 하는 방법이 있지만, 코드 가독성을 위해 타입 가드 방법을 주로 사용한다.\r\n\r\n```typescript\r\nconst isDeveloper = (target: Developer | Humanoid): target is Developer => {\r\n return (target as Developer).skill !== undefined;\r\n};\r\nif (isDeveloper(tom)) {\r\n console.log(tom.name);\r\n console.log(tom.skill);\r\n} else {\r\n console.log(tom.name);\r\n console.log(tom.age);\r\n}\r\n```\r\n\r\n# 타입 호환 (Type compatibility)\r\n\r\n### TypeScript에서 더 큰 타입 구조를 갖는 변수에 작은 타입 구조를 갖는 변수를 할당 가능\r\n\r\n```typescript\r\nlet add = function (a: number) {\r\n // ...\r\n};\r\nlet sum = function (a: number, b: number) {\r\n // ...\r\n};\r\n// 아래는 에러가\r\n// add = sum;\r\n\r\n// 에러가 나지 않는 방식. sum의 구조가 더 크다고 볼 수 있음\r\nsum = add;\r\n```\r\n\r\n참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/\r\n"},{"title":"javascript how to use promise.all","description":"Promise.all()은 언제 쓰고 왜 쓰나요? - 여러 비동기 요청 한 번에 처리하기","category":"javascript","date":"2023-05-19","content":"\r\njavascript를 통해 비동기 코드를 처리하는 기본적인 방법으로는 Callback 함수, Promise, async/await 등이 있다. 하지만 다수의 비동기 요청의 실행이 필요한 경우 Promise.all()을 사용해볼 수 있다.\r\n\r\n## **Promise.all()이란?**\r\n\r\n여러 개의 Promise를 동시에 처리하고, 모든 Promise가 완료되었을 때 그 결과들을 반환하는 javascript의 메서드이다.\r\n\r\nPromise.all()은 배열형태의 Promise들을 인자로 받고, 이 Promise들은 병렬적으로 실행된다. Promise.all()은 해당 Promise들의 결과를 배열로 반환한다.\r\n\r\n**만약 하나라고 Promise가 거부되면, 첫 번째 거부된 Promise를 반환하고, 나머지 Promise들은 무시된다.**\r\n\r\n## **Promise.all()을 사용하는 이유**\r\n\r\nPromise.all()을 사용하면 병렬적으로 실행되는 Promise들을 효율적으로 관리하고, 모든 Promise의 결과를 한 번에 처리할 수 있다. 이는 비동기 작업을 동시에 처리하고 작업이 완료되었을 때 결과를 수집해야 하는 상황에서 유용하다.\r\n\r\n예를 들어 3초가 소요되는 비동기 요청 a, 2초가 소요되는 비동기 요청 b, 1초가 소요되는 비동기요청 c를 async/await을 통해 순차적으로 처리하게 되는 경우 총 6초가 소요되지만, Promise.all()을 통해 이전 요청이 완료되는 것을 기다리지 않고 비동기적으로 병렬로 실행시킨다면 3초만에 실행을 완료시킬 수 있는 것이다.\r\n\r\n```javascript\r\n\r\n// acyns/await\r\n\r\ncosnt test1 = async () =>{\r\n\tawait a() // 3000ms\r\n await b() // 2000ms\r\n await c() // 1000ms\r\n}\r\n// 약 6초 소요\r\n\r\nconst test2 = async () =>{\r\n\tconst [res1, res2, res3] = await Promise.all([\r\n \ta(),\r\n b(),\r\n c()\r\n ])\r\n return [res1, res2, res3]\r\n}\r\n// 약 3초 소요\r\n\r\n```\r\n\r\n## **Promise.all() 사용 상황**\r\n\r\n아래와 같이 특정 DB의 데이터를 연속적으로 도출하기 위해 async/await과 Promise.all()을 사용해봤다.\r\n\r\n```javascript\r\n\r\n// 1) async/await\r\nasync func1(){\r\n\tconst community_with_likes = await community.map(async (item) => {\r\n const like_list = await this.communityLikesRepository.find({\r\n where: {\r\n community_id: item.community_id,\r\n },\r\n });\r\n return like_list;\r\n });\r\n}\r\n\r\n// 2) Promise.all()\r\nasync func2(){\r\n\tconst community_with_likes = await Promise.all(\r\n community.map(async (item) => {\r\n const like_list = await this.communityLikesRepository.find({\r\n where: {\r\n community_id: item.community_id,\r\n },\r\n });\r\n return like_list;\r\n }),\r\n );\r\n}\r\n\r\n```\r\n\r\n### **1. async/await**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOMvFZ%2FbtsgB32dgj6%2FbaANEHQzX0hxTVckIjOPiK%2Fimg.png)\r\n\r\nasync/await을 사용해 결과를 출력하려고 할 때는 위와 같이 원하는 방식대로 데이터가 출력되지 않았다.\r\n\r\n### **2. Promise.all()**\r\n\r\n하지만 Promise.all()을 사용해 결과를 도출하니 아래와 같이 원하는 결과값을 볼 수 있었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo73VZ%2FbtsgCGeohNN%2F42YW4sTfKAiXcl3VeJgP51%2Fimg.png)\r\n\r\n비동기 처리를 다루는 방법은 여러가지가 있지만, 내가 적용하고자 하는 작업의 특성에 맞게 알맞는 방법을 골라서 사용하는 것이 좋겠다.\r\n\r\n---\r\n\r\n참조\r\n\r\nhttps://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all\r\n\r\nhttps://code-masterjung.tistory.com/91\r\n"},{"title":"nestjs localhost https","description":"Localhost 환경에서 HTTPS 적용하기","category":"nestjs","date":"2023-04-30","content":"\r\n### Local 환경에서 https 설정이 필요한 이유\r\n\r\n로컬 환경에서 내가 원하는대로 기능들이 작동한다고 해도, 배포 환경과 로컬 개발 환경의 차이로 인해 코드의 수정이 필요해질 수 있다. 이러한 이유로 개발 환경을 배포 환경과 최대한 동일하게 만들어주는 것이 좋다. 또한 최근의 배포 환경들은 대부분 https로 이뤄져 있기 때문에 그에 맞춰 설정해줄 필요가 있다.\r\n\r\n### 자체 서명 인증서 생성 (openssl 사용)\r\n\r\n```bash\r\n# 개인 키 생성\r\n$ openssl genrsa -out private-key.pem 2048\r\n\r\n# 개인 키를 사용한 새로운 인증서 요청서 생성\r\n$ openssl req -new -key private-key.pem -out cert-request.csr\r\n\r\n# 요청서를 사용한 자체 서명 인증서 생성\r\n$ openssl x509 -req -in cert-request.csr -signkey private-key.pem -out cert.pem\r\n```\r\n\r\n인증서 생성 시 입력사항은 모두 건너뛰어도 무관하다.\r\n\r\n### HTTPS 구성 설정 - main.ts\r\n\r\n```typescript\r\nimport * as fs from \"fs\";\r\nimport * as https from \"https\";\r\n\r\nasync function bootstrap() {\r\n const httpsOptions = {\r\n key: fs.readFileSync(\"./private-key.pem\"),\r\n cert: fs.readFileSync(\"./cert.pem\"),\r\n };\r\n const app = await NestFactory.create(AppModule, {\r\n httpsOptions,\r\n });\r\n // 필요에 따라 cors 설정도 가능\r\n // app.enableCors({\r\n // origin: [url,...],\r\n // credentials: true, 쿠키를 사용하는 경우 설정\r\n // });\r\n await app.listen(3000);\r\n}\r\nbootstrap();\r\n```\r\n\r\n설정이 완료되면 로컬에서 https 환경처럼 적용할 수 있다.\r\n\r\n하지만 자체 인증서로 https를 만들어 SSR(ServerSideRendering)이나 API 요청 시 self certifi 에러가 발생할 수 있다. 이런 경우 프론트단 env에 다음과 같이 설정해주면 된다.\r\n\r\n```bash\r\n# .env\r\nNODE_TLS_REJECT_UNAUTHORIZED=0\r\n```\r\n\r\n그럼에도 오류가 발생하는 경우 아래의 링크를 통해 크롬 환경설정을 해주면 된다.\r\n\r\nchrome://flags/#allow-insecure-localhost\r\n\r\n유효하지 않은 인증서 허용으로 설정\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3WChm%2Fbtsdi3DwSL4%2F5cdQ9YFzgaKugT4u9ewDk0%2Fimg.png)\r\n"},{"title":"nestjs server client cookie2","description":"NestJS에서 클라이언트로 쿠키 보내기 (왜 내 쿠키는 안받아줘요?) - 배포환경","category":"nestjs","date":"2023-04-30","content":"\r\n### **[서버에서 브라우저로 쿠키 저장](https://lee-yo-han.github.io/nestjs-server-client-cookie)**\r\n\r\n분명 로컬에서는 쿠키가 정상적으로 브라우저에 저장되는 것을 볼 수 있었는데, 배포를 하고 나니 또다시 쿠키가 보이지 않았다. response headers에는 쿠키 값과 함께 다음과 같은 에러 문구를 볼 수 있었다.\r\n\r\n> **this attempt to set a cookie via a set cookie header was blocked because its domain attribue was invalid with reqards to the current host url**\r\n>\r\n> > \"설정된 쿠키 헤더를 통해 쿠키를 설정하려는 이 시도는 해당 도메인 속성이 현재 호스트 URL에 대해 유효하지 않기 때문에 차단되었습니다.\"\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGZzIY%2FbtsdemcKoT1%2FcT16wBLzk5J48gv37fGyPK%2Fimg.png)\r\n\r\n쿠키 전송 옵션은 다음과 같다.\r\n\r\n```typescript\r\nres.cookie(\"cookie\", \"cookie\", {\r\n maxAge: 0,\r\n sameSite: process.env.COOKIE_PARSE_SAME_SITE as SameSite, // \"none\"\r\n secure: true,\r\n httpOnly: true,\r\n domain: process.env.COOKIE_PARSE_DOMAIN, // \"www.backend.com\"\r\n});\r\n```\r\n\r\n프론트엔드의 URL이 https://www.frontend.com 이고,\r\n\r\n백엔드의 URL이 https://www.backend.com 이라고 했을 때, domain 속성을 백엔드 URL로 설정했을 때 발생한 오류이며, domain을 프론트엔드 URL로 바꾸면 response headers에 쿠키 값 자체가 확인되지 않았다.\r\n\r\ncors 설정도 credential: true나 origin 등을 정상적으로 해놓은 상태이기 때문에 쿠키 외 다른 데이터는 정상적으로 받아오는걸 확인할 수 있었다.\r\n\r\n다른 자료를 찾아보니, secure 속성을 적용하면 쿠키가 정상적으로 저장된다고는 하지만 본인은 이런 문제가 해결되지 않았다.\r\n\r\n결국 도메인을 구매해서 사용하게 되었다.\r\n\r\n예를 들어 프론트엔드의 URL은 https://www.product.com 로,\r\n\r\n백엔드의 URL은 https://api.product.com 으로 연동시켜주고, 쿠키 옵션은 아래처럼 바꿨다.\r\n\r\n```typescript\r\nres.cookie(\"cookie\", \"cookie\", {\r\n maxAge: 0,\r\n sameSite: process.env.COOKIE_PARSE_SAME_SITE as SameSite, // \"lax\"\r\n secure: true,\r\n httpOnly: true,\r\n domain: process.env.COOKIE_PARSE_DOMAIN, // \".product.com\"\r\n});\r\n```\r\n\r\n같은 도메인을 사용하고 나서야 쿠키가 정상적으로 브라우저로 접속하는 것을 확인할 수 있었다.\r\n"},{"title":"nextjs pages api dir unsupported","description":"NextJS에서 API 디렉토리가 사용되지 않는 이유","category":"nextjs","date":"2023-04-30","content":"\r\nNextJS는 pages/api 디렉토리를 통해 백엔드에 대한 처리를 수행할 수 있다.\r\n\r\n하지만 정적으로 배포된 사이트(ex. 블로그 등)에서는 pages/api 등과 같은 동적 로직은 지원되지 않기 때문에 api 디렉토리를 사용할 수 없다.\r\n\r\n정적 배포 방법\r\n\r\n```javascript\r\n// package.json\r\n// NextJS 13.3 이전 버전\r\n\"scripts\": {\r\n \"dev\": \"next dev\",\r\n \"start\": \"next start\",\r\n \"lint\": \"next lint\",\r\n \"build\": \"next build && next export\", // ## 정적 배포\r\n \"predeploy\": \"npm run build\",\r\n \"deploy\": \"touch out/.nojekyll && gh-pages -d out --dotfiles\"\r\n },\r\n\r\n\r\n// next.config.js\r\n// NextJS v13.3 ~\r\n/**\r\n * @type {import('next').NextConfig}\r\n */\r\nconst nextConfig = {\r\n output: 'export',\r\n}\r\n\r\nmodule.exports = nextConfig\r\n```\r\n\r\n---\r\n\r\n참조\r\n\r\nhttps://nextjs.org/docs/advanced-features/static-html-export\r\n"},{"title":"nextjs prop classname did not match","description":"prop `classname` did not match. with styled-components","category":"nextjs","date":"2023-04-30","content":"\r\n## prop 'classname' did not match. with styled-components 에러 해결 방법\r\n\r\nNextJS의 next.config를 수정해준다.\r\n\r\n```javascript\r\n/** @type {import('next').NextConfig} */\r\nconst nextConfig = {\r\n reactStrictMode: false,\r\n compiler: {\r\n styledComponents: true, // 컴파일러 옵션 추가\r\n },\r\n};\r\n\r\nmodule.exports = nextConfig;\r\n```\r\n"},{"title":"nestjs server client cookie","description":"NestJS에서 클라이언트로 쿠키 보내기 (왜 내 쿠키는 안받아줘요?)","category":"nestjs","date":"2023-04-13","content":"\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpD9qr%2Fbtr9Oa0APIT%2FFuzZyjjney7aKkIXy6BAfK%2Fimg.png)\r\n\r\n## 쿠키(Cookie)란?\r\n\r\n쿠키는 서버에서 클라이언트에게 보내는 작은 데이터 조각이다. 일반적으로 브라우저에서 웹 사이트를 방문할 때 쿠키를 사용하여 사용자의 활동을 기록하고, 이후에 그 사용자가 같은 웹 사이트를 방문할 때 이 정보를 사용해 그에 맞게 동작하게 해준다. 하지만 쿠키는 당사자뿐만 아닌 제 3자가 조회하는 것도 가능하기 때문에 개인 정보를 담는 등 보안상 민감한 정보를 저장하는 데에는 적합하지 않기 때문에 탈취되거나 사용자에 의해 조작되어도 크게 문제 되지 않을 정보를 주로 저장한다. (ex. 다크 모드, 장바구니 목록 등)\r\n\r\n## 서버에서 클라이언트로 Cookie 전송 예제\r\n\r\n### 모듈 설치\r\n\r\n```bash\r\nyarn add cookie-parser @types/cookie-parser # 쿠키 관리 모듈\r\n```\r\n\r\n### 모듈 적용\r\n\r\n```typescript\r\n// main.ts\r\n\r\nimport { NestFactory } from \"@nestjs/core\";\r\nimport { AppModule } from \"./app.module\";\r\nimport * as cookieParser from \"cookie-parser\";\r\n\r\nasync function bootstrap() {\r\n const app = await NestFactory.create(AppModule);\r\n app.enableCors({\r\n // cors 설정\r\n origin: \"http://localhost:3000\",\r\n credentials: true, // 쿠키를 사용할 수 있게 해당 값을 true로 설정\r\n });\r\n app.use(cookieParser()); // 쿠키의 편리한 이용을 위해 cookieParser 적용\r\n await app.listen(3001);\r\n}\r\nbootstrap();\r\n```\r\n\r\n### 쿠키 사용 로직\r\n\r\n```typescript\r\n// controller.ts\r\n\r\nimport { Response } from \"express\";\r\nimport { Controller, Get, Res } from \"@nestjs/common\";\r\n\r\n@Controller(\"myController\")\r\nexport class MyController {\r\n @Get()\r\n getCookie(@Res() res: Response) {\r\n // express의 Response 객체를 불러와 사용해준다.\r\n // 쿠키 설정\r\n res.cookie(\"cookieName\", \"cookieValue\", { maxAge: 900000, httpOnly: true });\r\n // maxAge : 유효기간을 밀리초 단위로 설정\r\n // httpOnly : 클라이언트에서 쿠키에 접근할 수 없도록 설정\r\n\r\n // HTTP 응답 반환\r\n return res.send(\"쿠키가 설정되었습니다.\");\r\n }\r\n}\r\n```\r\n\r\n쿠키를 요청하는 클라이언트에서도 withCredentials 값을 true로 설정해준다.\r\n\r\n```typescript\r\naxios.get(BASE_URL, {\r\n withCredentials: true,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n },\r\n});\r\n```\r\n\r\n## 문제상황\r\n\r\n보통 예제는 위와 같지만 나는 쿠키가 브라우저에 저장되지 않았다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd0P7QS%2Fbtr9SjoI9ke%2FIW9CJf8K5mS7kDuau67KDK%2Fimg.png)\r\n\r\n로직이 잘못된 것처럼 보이지도 않았고 별도의 에러 메시지도 없어서 문제 파악이 어려웠다.\r\n\r\n네트워크 탭을 통해 확인해 보니 response 헤더에 쿠키도 정상적으로 들어가 있었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDRJgW%2Fbtr9NnsBfTs%2FlGo4A2uw6n2UXPJJQVwbb1%2Fimg.png)\r\n\r\n그런데 노란 경고판이 눈에 띄었다.\r\n\r\n경고 문구는 다음과 같다.\r\n\r\n> **this set-cookie header didn't specify a samesite attribute, was defaulted to sameSite=Lax, and was blocked because it came from a cross-site reponse which was not the response to a top-level navigation. this response is considered cross-site because the URL has a different scheme than the current site**\r\n\r\n요청 URL과 서버 URL의 체계가 달라서 차단됐다고 한다. 내 클라이언트는 http://localhost인데 서버가 https://localhost라서 그런 것 같았다.\r\n\r\nsameSite를 none으로 설정해주고 난 후에는 다음과 같은 경고 문구가 생겼다.\r\n\r\n> **this attempt to set a cookie via a set-cookie header was blocked because it had the \"SameSite=None\" attribute but did not have the \"Secure\" attibute, which is required in order to user \"sameSite=None\"**\r\n\r\nsameSite=none을 적용하기 위해 필요한 Secure 속성이 없기 때문에 쿠키 설정이 차단되었단다.\r\n\r\n쿠키 생성 옵션을 마저 추가해 줬다.\r\n\r\n```typescript\r\nres.cookie(\"cookieName\", \"cookieValue\", {\r\n maxAge: 300000,\r\n // none, lax, strict 중 none은 쿠키가 항상 전송되도록 허용.\r\n sameSite: \"none\", // HTTPS 프로토콜을 사용하고 secure 옵션이 설정된 경우에만 사용 가능\r\n secure: true, // 쿠키가 HTTPS 프로토콜을 사용하는 경우에만 전송되도록 제한\r\n httpOnly: true, // 쿠키에 접근할 수 있는 영역을 HTTP(S) 프로토콜로 제한하여,\r\n // 브라우저의 자바스크립트 코드로부터 쿠키에 접근할 수 없게 함\r\n});\r\n```\r\n\r\nsecure 속성을 사용하기 위한 https 설정은 아래를 참고할 수 있다.\r\n\r\n### [Localhost 환경에서 https 적용하기](https://lee-yo-han.github.io/nestjs-localhost-https)\r\n\r\n그 결과 쿠키를 브라우저에서 잘 받는 것을 볼 수 있었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJwJs6%2Fbtr9SkupB08%2FbXf0Hf9swCaY3uL8cxMh7K%2Fimg.png)\r\n\r\n마지막으로 서버에서 쿠키를 받아 사용하는 방법이다.\r\n\r\n```typescript\r\nimport { Response, Request } from \"express\";\r\nimport { Controller, Get, Post, Res, Req } from \"@nestjs/common\";\r\n\r\n@Controller(\"myController\")\r\nexport class MyController {\r\n @Get()\r\n getCookie(@Res() res: Response) {\r\n // express의 Response 객체를 불러와 사용해준다.\r\n // 쿠키 설정\r\n res.cookie(\"cookieName\", \"cookieValue\", { maxAge: 900000, httpOnly: true });\r\n // maxAge : 유효기간을 밀리초 단위로 설정\r\n // httpOnly : 클라이언트에서 쿠키에 접근할 수 없도록 설정\r\n\r\n // HTTP 응답 반환\r\n return res.send(\"쿠키가 설정되었습니다.\");\r\n }\r\n\r\n @Get()\r\n postCookie(@Req() req: Request) {\r\n // express의 Request 객체를 불러와 사용해준다.\r\n // request 객체의 cookies를 통해 생성했던 쿠키의 이름을 통해 값을 가져온다.\r\n req.cookies[\"cookieName\"];\r\n }\r\n}\r\n```\r\n\r\n클라이언트 단에서는 withCredentials:true 옵션 설정 외에는 특별히 해줄 것이 없다.\r\n\r\n---\r\n\r\n참조 : https://docs.nestjs.com/techniques/cookies\r\n"},{"title":"nestjs use bcrypt","description":"Nestjs에서 bcrypt 사용하기","category":"nestjs","date":"2023-04-13","content":"\r\n## bycrypt란?\r\n\r\n비밀번호 등을 안전하게 암호화하여 저장하고 검증할 수 있도록 도와주는 라이브러리로, 랜덤한 salt를 생성하고 이를 비밀번호와 함께 암호화하여 저장한다. 이후 비밀번호 검증 시에도 같은 salt를 사용하여 입력받은 비밀번호를 암호화한 후, 저장된 암호화된 비밀번호와 비교하여 일치하는지 검증한다.\r\n\r\nbcrypt는 암호화 강도를 선택할 수 있는 기능을 제공한다. 기본값은 10으로, 숫자가 클수록 강도가 높아지며 암호화에 소요되는 시간도 늘어난다. 보안을 위해 기본값 이상의 값을 권장한다.\r\n\r\n## 사용법\r\n\r\n```bash\r\nyarn add bcrypt @types/bcrypt # nestjs는 타입스크립트가 기본이기 때문에 타입으로 설치\r\n```\r\n\r\n따로 module이나 main에 등록할 필요 없이 사용하고자 하는 파일에 불러오면 된다.\r\n\r\n```typescript\r\nimport * as bcrypt from 'bcrypt';\r\n\r\n// 암호화 후 저장\r\nconst someFN = async (userData:UserData) => {\r\n\tconst hashPassword = await bcrypt.hash(userData.password,10)\r\n\tconst saveData = {\r\n \temail:userData.email,\r\n password: hashPassword,\r\n }\r\n // 정보 저장 로직\r\n}\r\n\r\n// 저장된 데이터를 통해 비밀번호 확인\r\nconst checkFN = async (userData:UserData) =>{\r\n\tconst hashedPassword = // 저장된 정보 불러오는 로직\r\n\r\n // true or false\r\n const match = await bcrypt.compare(userData.password,hashedPassword)\r\n if(match) // 인증 후 로직\r\n}\r\n```\r\n\r\n---\r\n\r\n참조 : https://github.com/kelektiv/node.bcrypt.js#readme\r\n"},{"title":"react common components","description":"React 공통 컴포넌트 제작 (input, button)","category":"react","date":"2023-04-13","content":"\r\n## 공통 컴포넌트 제작의 필요성\r\n\r\n> 하나의 사이트를 제작할때 비슷한 input이나 button을 사용해야 하는 상황이 자주 있는데, 공통된 컴포넌트를 만들어 놓으면 코드 중복을 줄이면서 상황에 맞춰 적절하게 사용할 수 있게 된다.\r\n\r\n## 컴포넌트 제작\r\n\r\n### Input\r\n\r\n```typescript\r\nimport React from \"react\"; // element 속성을 가져오기 위해 import\r\nimport styled from \"styled-components\"; // CSS 적용\r\n\r\n// 컴포넌트의 props 타입을 InputElement의 속성과 내가 설정하고자 하는 타입을 포함한다.\r\ntype Props = React.HTMLAttributes & InputProps;\r\n\r\ninterface InputProps {\r\n type?: string | undefined; // type?: 의 ?는 있어도 되고 없어도 된다는 의미\r\n name?: string | undefined;\r\n autoComplete?: string | undefined;\r\n width: string;\r\n height: string;\r\n border?: string | undefined;\r\n}\r\n\r\n// width와 height는 number로 지정해도 무관하나,\r\n// 상황에 따라 뷰포트에 맞춰 쓸지, px에 맞춰 쓸지 달라질 수 있기 때문에 string으로 설정한 경우\r\n\r\nexport const MainInput = ({\r\n type,\r\n name,\r\n autoComplete,\r\n width,\r\n height,\r\n border,\r\n ...props\r\n}: Props) => {\r\n return (\r\n \r\n );\r\n};\r\n\r\nconst Main = styled.input`\r\n padding: 5px;\r\n border: none;\r\n border: 1px solid ${props => props.theme.inputBorderColor};\r\n border-radius: 5px;\r\n font-size: 1.2rem;\r\n &:hover {\r\n background: ${props => props.theme.inputBorderColor};\r\n }\r\n &:focus {\r\n outline: none;\r\n border-color: ${props => props.theme.hoverBorderColor};\r\n box-shadow: 0 0 1px ${props => props.theme.hoverBorderColor};\r\n }\r\n`;\r\n```\r\n\r\n### TextArea\r\n\r\n```typescript\r\ntype TaProps = React.HTMLAttributes & TextAreaProps;\r\ninterface TextAreaProps {\r\n name?: string | undefined;\r\n width: string;\r\n height: string;\r\n border?: string | undefined;\r\n}\r\n\r\nexport const MainTextArea = ({\r\n name,\r\n width,\r\n height,\r\n border,\r\n ...props\r\n}: TaProps) => {\r\n return (\r\n \r\n );\r\n};\r\n\r\nconst TextArea = styled.textarea`\r\n padding: 5px;\r\n border: none;\r\n border: 1px solid ${props => props.theme.inputBorderColor};\r\n border-radius: 5px;\r\n font-size: 1.2rem;\r\n resize: none;\r\n white-space: pre;\r\n &:hover {\r\n background: ${props => props.theme.inputBorderColor};\r\n }\r\n &:focus {\r\n outline: none;\r\n border-color: ${props => props.theme.hoverBorderColor};\r\n box-shadow: 0 0 1px ${props => props.theme.hoverBorderColor};\r\n }\r\n`;\r\n```\r\n\r\n## Button\r\n\r\n```typescript\r\ntype Props = React.HTMLAttributes & ButtonProps;\r\n\r\ninterface ButtonProps {\r\n type?: \"button\" | \"submit\" | \"reset\" | undefined; // 버튼 타입은 이와 같이 설정해준다.\r\n content: string;\r\n width: string;\r\n height: string;\r\n}\r\n\r\nexport const MainButton = ({\r\n type,\r\n content,\r\n width,\r\n height,\r\n ...props\r\n}: Props) => {\r\n return (\r\n \r\n {content}\r\n \r\n );\r\n};\r\n\r\nconst Main = styled.button`\r\n border: none;\r\n border-radius: 5px;\r\n background: none;\r\n background-color: ${props => props.theme.mainButton};\r\n color: ${props => props.theme.mainFontColor};\r\n font-size: 1rem;\r\n font-weight: bold;\r\n cursor: pointer;\r\n &:hover {\r\n background-color: ${props => props.theme.mainButtonHover};\r\n }\r\n`;\r\n```\r\n"},{"title":"react type assertion","description":"React Form event type (feat. 타입 단언 as)","category":"react","date":"2023-04-13","content":"\r\n## Form Event Type\r\n\r\n우리는 로그인이나 포스팅 등의 화면을 만들어줄 때 클라이언트가 입력하는 데이터를 입력받는 방법 중 하나로 onChange 등의 FormEvent를 이용해 원하는 값을 추출할 수 있다. 이때 이벤트 타입은 아래와 같이 사용될 수 있다.\r\n\r\n```typescript\r\nimport { FormEvent } from \"react\";\r\n\r\nexport type FormEvents = FormEvent;\r\nexport type InputEvent = FormEvent;\r\nexport type LabelEvent = FormEvent;\r\n```\r\n\r\nreact에서 FormEvent 타입을 받아오고, 해당되는 element를 제네릭 타입으로 넣어주면 된다.\r\n\r\n## onChangeHandler\r\n\r\n```typescript\r\nconst [formData, setFormData] = useState(formDataInit);\r\nconst formChangeHandler = (e: FormEvent) => {\r\n const target = e.target;\r\n const name = target.name;\r\n const value = target.value;\r\n setFormData(prev => ({ ...prev, [name]: value }));\r\n};\r\n```\r\n\r\n원하는 데이터를 받을 수는 있지만, name과 value의 타입 에러가 발생한다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F80Q0s%2Fbtr9SOPHzuW%2F7t6EmYi1k5gMiyeurxmCP0%2Fimg.png)\r\n\r\ntarget의 타입을 정해줘도 타입 에러가 발생하는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbsb5ww%2Fbtr9N2aq2VJ%2FsFJkJduLoSt1bhyae9SFdK%2Fimg.png)\r\n\r\n하지만 우리는 form의 input이 변할때마다 대상의 값을 받아오는 것을 알고 있다.\r\n\r\n이때 필요한 것이 타입 단언(as)이다.\r\n\r\n## 타입 단언 사용 예시\r\n\r\n```typescript\r\nconst formChangeHandler = (e: FormEvents) => {\r\n const target = e.target as InputTarget; // InputTarget === HTMLInputElement\r\n const name = target.name;\r\n const value = target.value;\r\n setFormData(prev => ({ ...prev, [name]: value }));\r\n};\r\n```\r\n\r\n### 주의사항\r\n\r\n타입 단언을 사용하면 타입 체크를 할 수 없다. 사실상 강제로 타입을 지정하는 행위이기 때문에 타입 체커에게 해당 타입 에러를 무시하라는 명령이나 다름없다. as를 남발하거나 내려받는 데이터가 확실하지 않는 경우에는 오류 탐색에 지장이 있을 수 있으니, 로직에 따라 조건문 등을 사용해 타입 에러를 피해 주는 것이 좋을 수 있다.\r\n\r\n```typescript\r\n// ex)\r\nconst someFN = (someData) =>{\r\n\tconst someMutation = someData..logic\r\n if(someMutation){\r\n \t// ... nest logic\r\n }\r\n}\r\n```\r\n"},{"title":"css use download font","description":"CSS - 배포 사이트에 다운로드 폰트 적용하기","category":"css","date":"2023-03-31","content":"\r\n> 기본적으로 CSS에서 제공하는 폰트가 마음에 들지 않거나 내 프로젝트에 어울리지 않는 경우 간단하게 다운로드한 폰트를 프로젝트에 적용해볼 수 있다.\r\n\r\n## 1. 폰트 다운로드\r\n\r\n우선 마음에 드는 폰트를 다운받는다.\r\n\r\n카페24무료폰트: https://fonts.cafe24.com/\r\n\r\n무료 폰트 사이트는 찾아보면 사용할 수 있는게 많다.\r\n\r\n## 2. 프로젝트에 파일 복사\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHA9Qr%2Fbtr60vtCf7O%2FMaaAsK1Lg4t5QDXd3bC1Uk%2Fimg.png)\r\n\r\n위와 같이 다운로드한 폰트를 작업중인 프로젝트에 넣어준다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA7Inw%2Fbtr61haATVa%2F9mFQHIOUCbjYI7SnPm8wI0%2Fimg.png)\r\n\r\n필자는 styles/fonts/\\*\\*.ttf 와 같이 저장해줬다.\r\n\r\n## 3. CSS 설정\r\n\r\n@font -face{}를 이용해 불러올 폰트를 설정해준다.\r\n\r\n```css\r\n/* global.css */\r\n\r\n@font-face {\r\n font-family: maplestory;\r\n src: url(\"./fonts/Maplestory\\ Light.ttf\");\r\n}\r\n@font-face {\r\n font-family: maplestoryBold;\r\n src: url(\"./fonts/Maplestory\\ Bold.ttf\");\r\n}\r\n```\r\n\r\n## 4. CSS 사용\r\n\r\n평소에 폰트를 적용하는 것처럼 CSS를 적용해주면 된다.\r\n\r\n```css\r\nbody {\r\n font-family: maplestory;\r\n}\r\n```\r\n"},{"title":"github markdown syntex","description":"Markdown 문법 정리","category":"github","date":"2023-03-31","content":"\r\n## 1. headers\r\n\r\n\\# 으로 시작하는 텍스트로 1-6개 가능 (h1 ~ h6)\r\n\r\n```\r\n# h1\r\n## h2\r\n### h3\r\n#### h4\r\n##### h5\r\n###### h6\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIOzmy%2Fbtr7d8qRKov%2FiXZSIKWKSOiAtliDzN63vK%2Fimg.png)\r\n\r\n## 2. 구분선\r\n\r\n\"---\"이나 \"\\*\\*\\*\"를 통해 구분선 생성 가능\r\n\r\n```\r\n***\r\n---\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgcFby%2Fbtr7dx5ZjTh%2FlEqZkNRdg7rLooCPmMFZW0%2Fimg.png)\r\n\r\n## 3. 줄바꿈\r\n\r\n\"
\"\r\n\r\n```\r\n줄을
바꿈\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8z1Q4%2Fbtr7fs3s9fI%2FhLPfWRYRfKMIC2tUvU8531%2Fimg.png)\r\n\r\n## 4. 강조\r\n\r\n기울여쓰기(italic) : \\* 또는 \\_로 감싼 텍스트.\r\n\r\n두껍게 쓰기(bold) : \\*\\* 또는 \\_\\_로 감싼 텍스트.\r\n\r\n취소선 : ~~로 감싼 텍스트\r\n\r\n```\r\n_기울여쓰기 1_\r\n*기울여쓰기 2*\r\n**두껍게 1**\r\n__두껍게 2__\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu2sXJ%2Fbtr7dNgeNxn%2FcCfQQ9PumZ7OzrH2KeXq5K%2Fimg.png)\r\n\r\n## 5. 인용\r\n\r\n\\>로 시작하는 텍스트로 >>>와 같이 3개까지 사용 가능\r\n\r\n```\r\n> 인용문\r\n> > 3개까지\r\n> > > 사용 가능\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGU0Me%2Fbtr7ghUUjee%2FqgoUnVkdOOkK7Kb5Gq1oPK%2Fimg.png)\r\n\r\n## 6. 리스트\r\n\r\n리스트는 Tab을 통해 목록 안의 목록 생성 가능\r\n\r\n### 순서가 없는 목록\r\n\r\n\\*, +, -를 통해 순서가 없는 목록 생성 가능\r\n\r\n```\r\n- 순서가 없는 목록1\r\n- 순서가 없는 목록1\r\n- 순서가 없는 목록1\r\n\r\n* 순서가 없는 목록2\r\n - 탭을 이용한 목록 안의 목록\r\n - 탭을 이용한 목록 안의 목록 안의 목록\r\n\r\n1. 순서가 있는 목록1\r\n2. 순서가 있는 목록1\r\n3. 순서가 있는 목록1\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQp7i8%2Fbtr7e1ygv06%2FvMIjB3OsI8Q5fAteCTJbF1%2Fimg.png)\r\n\r\n### 순서가 있는 목록\r\n\r\n숫자를 기입하면 순서가 있는 목록이 됨\r\n\r\n들여쓰기를 하면 모양이 바뀜\r\n\r\n숫자를 무엇을 쓰든 순서대로 알아서 숫자를 매김\r\n\r\n리스트 안에 하위 리스트를 만들기 위해서는 tab과 함께 숫자 1번부터 나열하면 적용 가능\r\n\r\n```\r\n1. 순서가 있는 목록2\r\n2. 순서가 있는 목록2\r\n 1. 탭을 이용한 순서가 있는 목록 안의 목록\r\n 1. 탭을 이용한 순서가 있는 목록 안의 목록 안의 목록\r\n 2. 탭을 이용한 순서가 있는 목록 안의 목록\r\n5. 순서가 있는 목록2\r\n```\r\n\r\n혼합 리스트도 적용 가능\r\n\r\n```\r\n1. 순서가 있는 목록2\r\n2. 순서가 있는 목록2\r\n 1. 탭을 이용한 순서가 있는 목록 안의 목록\r\n * 탭을 이용한 순서가 있는 목록 안의 목록 안의 순서가 없는 목록\r\n 2. 탭을 이용한 순서가 있는 목록 안의 목록\r\n5. 순서가 있는 목록2\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlhYHi%2Fbtr7dPd52Gh%2FY87O8eblzYL1LiuEiIdMik%2Fimg.png)\r\n\r\n## 7. 이미지\r\n\r\n링크와 비슷하나 앞에 느낌표가 붙음\r\n\r\n인라인 이미지 : ![텍스트](이미지파일 경로)\r\n\r\n![image](\"./../../../../public/images/cards/GITHUB.png)\r\n\r\n링크 이미지 : ![텍스트] (이미지파일URL)\r\n\r\n![image](https://img1.daumcdn.net/thumb/C428x428/?scode=mtistory2&fname=https%3A%2F%2Ftistory1.daumcdn.net%2Ftistory%2F5148697%2Fattach%2Fc60c2213f3984f0b9da48413e3fa277e)\r\n\r\n이미지 파일에 마우스를 올렸을 때 커서 앞에 나오는 텍스트 설정\r\n\r\n![텍스트] (이미지경로/URL \"이미지이름\")\r\n\r\n![image](https://img1.daumcdn.net/thumb/C428x428/?scode=mtistory2&fname=https%3A%2F%2Ftistory1.daumcdn.net%2Ftistory%2F5148697%2Fattach%2Fc60c2213f3984f0b9da48413e3fa277e \"이미지지롱\")\r\n\r\n링크와 이미지를 합친 문법 (이미지를 링크로 사용)\r\n\r\n[![텍스트] (이미지URL)] (링크URL)\r\n\r\n[![image](https://img1.daumcdn.net/thumb/C428x428/?scode=mtistory2&fname=https%3A%2F%2Ftistory1.daumcdn.net%2Ftistory%2F5148697%2Fattach%2Fc60c2213f3984f0b9da48413e3fa277e)](https://lee-yo-han.github.io/github-token-expired)\r\n\r\n## 8. Link (Anchor)\r\n\r\n글자로 된 하이퍼링크\r\n\r\n```\r\n[구글] (링크)\r\n```\r\n\r\n[구글](https://www.google.com/)\r\n\r\n내부(해시) 링크\r\n\r\n[보여지는 내용] (#이동할 헤드(제목))\r\n\r\n괄호 안 링크의 띄어쓰기는 -로 연결, 영어는 모두 소문자로 작성\r\n\r\n```\r\n[어디로든문](#1-headers)\r\n```\r\n\r\n[어디로든문](#1-headers)\r\n\r\n## 9. 코드 블럭\r\n\r\n백틱(`) 3개씩으로 감싸서 사용\r\n\r\n```\r\n'''javascript\r\nsome code ...\r\n'''\r\n```\r\n\r\n## 10. 테이블\r\n\r\n헤더와 셀 구분 시 3개 이상의 하이픈(-) 필요\r\n헤더 셀을 구분하면서 콜론(:)으로 정렬 가능\r\n가장 좌측과 가장 우측에 있는 vertical bar( | ) 기호 생략 가능\r\n\r\n```\r\n| 헤더1 | 헤더2 | 헤더3 | 헤더4 |\r\n| ----- | :------- | :--------: | -------: |\r\n| 셀1 | 셀2 | 셀3 | 셀4 |\r\n| 기본 | 좌로정렬 | 가운데정렬 | 우로정렬 |\r\n| 셀9 | 셀10 | 셀11 | 셀12 |\r\n```\r\n\r\n| 헤더1 | 헤더2 | 헤더3 | 헤더4 |\r\n| ----- | :------- | :--------: | -------: |\r\n| 셀1 | 셀2 | 셀3 | 셀4 |\r\n| 기본 | 좌로정렬 | 가운데정렬 | 우로정렬 |\r\n| 셀9 | 셀10 | 셀11 | 셀12 |\r\n\r\n---\r\n\r\n참조 https://inpa.tistory.com/entry/MarkDown-%F0%9F%93%9A-%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4-%EB%AC%B8%EB%B2%95-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC\r\n"},{"title":"github token expired","description":"Github Token을 이용한 GithubAPI 사용","category":"github","date":"2023-03-31","content":"\r\n### **본 포스팅은 NextJS 13 환경에서 진행한 내용을 바탕으로 한다. GithubAPI 사용법은 공식 문서에 상세히 나와있기 때문에 사용법보다 토큰 만료 문제를 주로 다룬다.**\r\n\r\n## GithubAPI란?\r\n\r\n> Github의 기능을 REST API 형식으로 사용할 수 있도록 도와주는 기능이다.\r\n\r\n**[GithubAPI docs](https://docs.github.com/en/rest?apiVersion=2022-11-28)**\r\n\r\n아래와 같이 사용할 수 있는 기능이 많다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6rpoI%2Fbtr65pd5Q0W%2F7NUKUqiXklINKYLwJWPow1%2Fimg.png)\r\n\r\n## 1. 토큰 발급\r\n\r\n간단한 조회 정도는 토큰이 필요하지 않지만, 요청의 종류에 따라 토큰이 필요한 경우가 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS8fhz%2Fbtr60iAL28F%2FxJ66qxT7PKF4U6JjLkCA91%2Fimg.png)\r\n\r\n우선 우측 상단의 Settings로 들어가서\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdU186G%2Fbtr61k5X0vE%2FdrgRV6uJMfho9JCIhoUghk%2Fimg.png)\r\n\r\nSettings의 좌측 하단 Developer settings에 들어간다.\r\n\r\n그 안에 Pat(personal access tokens)를 발급받을 수 있는 창을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE3TPa%2Fbtr60DxUYrw%2FtWMeaxCI78Cyx9838COCVK%2Fimg.png)\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnE2jt%2Fbtr60gbWaov%2FQ897sC2Gk2RxKlsBVZ5jLK%2Fimg.png)\r\n\r\nGenerate new token을 통해 원하는 종류의 토큰을 받을 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHPjvr%2Fbtr61gW4V3n%2Fvqk7jPvdQAcdlAfXomGiQ0%2Fimg.png)\r\n\r\n토큰명, 유효기간, access할 repo를 설정하고\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX6MQh%2Fbtr61lcFYwt%2F1fkFl327Q0I91UIwAgfCfK%2Fimg.png)\r\n\r\n원하는 기능에 대한 권한을 설정해준다.\r\n\r\n이번엔 간단하게 issue에 대한 작업만 진행해본다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVESxa%2Fbtr63YA3qrw%2FCedCRRISe4SG6PLHK4H0dK%2Fimg.png)\r\n\r\n생성된 토큰은 잘 복사해 안전한 공간에 저장해준다.\r\n\r\n## 2. Token 사용\r\n\r\n기본적인 사용법은 아래와 같다.\r\n\r\nhttps://docs.github.com/en/rest/guides/getting-started-with-the-rest-api?apiVersion=2022-11-28\r\n\r\n```typescript\r\n// issues 등록하기\r\nimport { Octokit } from \"octokit\";\r\n\r\nconst octokit = new Octokit({\r\n auth: `api token`,\r\n});\r\n\r\nconst issueUpdate = async (name: string, feed: string) => {\r\n await octokit.request(\r\n \"POST /repos/{owner}/{repository name}/issues/1/comments\",\r\n {\r\n owner: \"user name\",\r\n repo: \"repository name\",\r\n title: `title`,\r\n body: `body`,\r\n // headers: {\r\n // \"X-GitHub-Api-Version\": \"2022-11-28\",\r\n // },\r\n },\r\n );\r\n};\r\n```\r\n\r\n위 요청을 실행하면 정상적으로 issue 등록이 되는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjcLkP%2Fbtr65o7kXPZ%2Fh739Lzn25eBdRhDzcPPU8K%2Fimg.png)\r\n\r\n## 유의할 점\r\n\r\n### 1. headers 설정\r\n\r\n우선 headers를 주석처리한 이유는 공식 문서에서는 github API version을 headers에 담으라고 돼있지만, 버전과 함께 요청을 실행하면 cors에러가 발생하는 것을 볼 수 있다.\r\n\r\n**[Github CORS ISSUE](https://github.com/orgs/community/discussions/40619)**\r\n\r\nheaders는 별도로 추가하지 말고 auth만 설정해준 후 사용해 주도록 한다.\r\n\r\n### 2. auth Token 설정\r\n\r\nAPI key와 같은 토큰은 대부분 .env를 통해 변수를 설정해 사용해줘야 한다.\r\n\r\n하지만 .env에 토큰을 설정하고 commit을 하게되면 gitignore를 설정해줘도 github가 자동으로 감지해 토큰을 만료시키기 때문에 .env.local서 테스트하고 .env의 토큰은 지운 뒤에 사용해주자.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuJQsS%2Fbtr62Xh6jLO%2FHkR7hA4k0cMffBKY55DHu1%2Fimg.png)\r\n\r\n---\r\n\r\n참조 : https://docs.github.com/en\r\n"},{"title":"react smooth scroll","description":"React/Javascript 부드러운 스크롤 이동 적용","category":"react","date":"2023-03-25","content":"\r\n# **부드러운 스크롤 이동 적용 방법**\r\n\r\n> behavior:\"smooth\" 속성을 scroll 이벤트와 scroll 메서드와 같이 사용하며 부드러운 스크롤 이동을 적용해볼 수 있다.\r\n\r\n본 보스팅은 Nextjs 13 환경에서 작성되었다.\r\n\r\n## **scroll() 메서드**\r\n\r\n### **scroll() , scrollTo()**\r\n\r\nscroll(x,y) || scroll(options), scrollTo(x,y) || scrollTo(options) 와 같이 사용할 수 있다.\r\n\r\n사실상 두 메서드는 동일하게 작동한다.\r\n\r\nxy 좌표를 통해서도 사용 가능하며 options를 적용한다면 아래와 같이 사용할 수 있다.\r\n\r\n```javascript\r\nwindow.scroll({\r\n top: 0,\r\n left: 0,\r\n behavior: \"smooth\", // smooth: 부드럽게 전환 , auto: 즉시 이동\r\n});\r\n\r\nwindow.scrollTo({\r\n top: 0,\r\n left: 0,\r\n behavior: \"smooth\", // smooth: 부드럽게 전환 , auto: 즉시 이동\r\n});\r\n```\r\n\r\n### **scrollBy()**\r\n\r\n**scrollBy(x,y) || scrollBy(options)**\r\n\r\nscroll()과 scrollTo()가 특정 좌표로 이동한다면, scrollBy는 길이만큼 스크롤을 이동시킨다.\r\n\r\n옵션과 예제는 아래와 같다.\r\n\r\n```javascript\r\n// 기본 사용\r\nwindow.scrollBy(x, y);\r\n\r\n// 한 페이지 아래\r\nwindow.scrollBy(0, window.innerHeight);\r\n\r\n// 한 페이지 위\r\nwindow.scrollBy(0, -window.innerHeight);\r\n\r\n// 옵션\r\nwindow.scrollBy({\r\n top: 0,\r\n left: 0,\r\n behavior: \"smooth\", // smooth: 부드럽게 전환 , auto: 즉시 이동\r\n});\r\n```\r\n\r\nscroll 메서드가 적용된 화면과 예제코드\r\n\r\n![image](https://blog.kakaocdn.net/dn/bV3l2O/btr5OcgXBcx/ickRekJIOy3VMmRLtzt2pK/img.gif)\r\n\r\n```javascript\r\n// 최상단으로 이동\r\nconst moveScroll = () => {\r\n scroll({ top: 0, behavior: \"smooth\" });\r\n};\r\n\r\n// 한 페이지 위로 이동\r\nconst moveScrollByUp = () => {\r\n scrollBy({ top: -window.innerHeight, behavior: \"smooth\" });\r\n};\r\n\r\n// 한 페이지 아래로 이동\r\nconst moveScrollByDown = () => {\r\n scrollBy({ top: window.innerHeight, behavior: \"smooth\" });\r\n};\r\n\r\n// top 500 위치로 이동\r\nconst moveScrollTo = () => {\r\n scrollTo({\r\n top: 500,\r\n behavior: \"smooth\",\r\n });\r\n};\r\n\r\n// jsx\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n;\r\n```\r\n\r\n## **Chrome 브라우저에서 behavior:\"smooth\" 속성이 적용되지 않을 때**\r\n\r\n아래와 같이 크롬 버전은 최신이지만, smooth 속성이 적용되지 않는 현상이 나타났다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx3Fal%2Fbtr5ORwXdp3%2FAHsT7DB2D5i76OjhYqcN9k%2Fimg.png)\r\n\r\n![image](https://blog.kakaocdn.net/dn/bourLn/btr5NyrsLbU/nkulo83V1NSn2kEMQb2xYK/img.gif)\r\n\r\n호환성에도 문제가 없지만 MDN의 예제를 실행해 봐도 크롬에선 smooth 속성이 사용되지 않았다.\r\n\r\nMDN 스크롤 예제 : https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmJ1Dl%2Fbtr5Nv2OZvD%2Fmk5c6Hks8ai7J4UKhL6wR1%2Fimg.png)\r\n\r\n크롬 브라우저 설정을 따로 해주면 해결된다고는 하지만, 내 PC에서만 작동하는 건 근본적인 해결방법이 아니기 때문에 배제했다.\r\n\r\n참조 : http://www.devdic.com/css/refer/properties/property:1195/scroll-behavior\r\n\r\n### **smoothscroll-polyfill - 적용 실패**\r\n\r\n사용법 : https://www.npmjs.com/package/smoothscroll-polyfill?activeTab=readme\r\n\r\n```typescript\r\n// 패키지 설치\r\n// yarn add smoothscroll-polyfill\r\n\r\n// 최상단 파일\r\n\r\nimport smoothscroll from \"smoothscroll-polyfill\";\r\n\r\nif (typeof window !== \"undefined\") {\r\n smoothscroll.polyfill();\r\n}\r\n```\r\n\r\n브라우저 호환성 적용을 위한 패키지라고 하는데, 본인에겐 효과가 없었다.\r\n\r\n## **scroll 이벤트를 통한 스크롤 이동 적용**\r\n\r\n스크롤만 이용해서 기능을 적용할 생각이었기에 직접 만들기로 했다.\r\n\r\naddEventListener를 통해 스크롤 이벤트를 감지해 주는 hook을 만들어줬다.\r\n\r\n```typescript\r\n// 스크롤 위 아래 여부 확인\r\nimport { useState, useEffect, useRef } from \"react\";\r\n\r\ninterface MutableRefObject {\r\n current: T;\r\n}\r\n\r\nexport const useScroll = () => {\r\n const [scrollCheck, setScrollCheck] = useState(\"\");\r\n const prevScrollPos: MutableRefObject = useRef(0);\r\n\r\n useEffect(() => {\r\n const handleScroll = () => {\r\n const currentScrollPos = window.pageYOffset; // window객체의 pageYOffset 를 통해 현재 스크롤 위치 저장\r\n if (currentScrollPos > prevScrollPos.current) {\r\n // 스크롤 위치에 따른 스크롤 진행 방향 검증\r\n setScrollCheck(\"DOWN\");\r\n } else {\r\n setScrollCheck(\"UP\");\r\n }\r\n prevScrollPos.current = currentScrollPos; // 사용된 현재 스크롤 위치를 다음 이벤트 비교대상으로 저장\r\n };\r\n\r\n window.addEventListener(\"scroll\", handleScroll); // event 등록\r\n return () => {\r\n window.removeEventListener(\"scroll\", handleScroll); // event clear\r\n };\r\n }, [prevScrollPos]);\r\n return {\r\n scrollCheck, // up/down 여부 return\r\n };\r\n};\r\n\r\n// hook 사용 컴포넌트\r\nimport { useScroll } from \"@/hooks/useScroll\";\r\nconst { scrollCheck } = useScroll();\r\nuseEffect(() => {\r\n if (scrollCheck === \"UP\") {\r\n console.log(\"scroll UP\");\r\n }\r\n if (scrollCheck === \"DOWN\") {\r\n console.log(\"scroll DOWN\");\r\n }\r\n}, [scrollCheck]);\r\n```\r\n\r\n원하는 값이 잘 나오는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF7CNJ%2Fbtr5QzP3p9N%2FErlbv8julxnc1PKZSGRC71%2Fimg.png)\r\n\r\n> 스크롤 이벤트 특성상 한 번 휠을 돌릴 때마다 많은 호출이 일어나기 때문에 필요한 경우에만 사용될 수 있도록 setTimeout 등을 통해 적용하고자 하는 코드에 맞게 함수 실행을 조절해줘야 한다.\r\n\r\n```typescript\r\nimport { useState, useEffect, useRef } from \"react\";\r\nimport { scrollToSmoothly } from \"@/components/profile/SmoothScroll\";\r\nimport { useScroll } from \"@/hooks/useScroll\";\r\nconst { scrollCheck } = useScroll();\r\nconst scrollRef = useRef({\r\n // window.innerHeight 값 저장\r\n height: 0,\r\n});\r\n\r\nconst smoothScrollHandler = () => {\r\n let maxHeight = window.innerHeight * 6; // 최대 스크롤 길이\r\n let currentHeight = scrollRef.current.height; // 현재 스크롤 저장\r\n if (scrollCheck === \"UP\" && currentHeight !== 0) {\r\n // scroll up이고 맨 위가 아닐 때\r\n scrollRef.current.height -= window.innerHeight; // useRef에 이동할 height 조정\r\n scrollToSmoothly(scrollRef.current.height, 500); // 이동할 height까지 500ms동안 이동시킬 함수 (requestAnimationFrame 사용)\r\n } else if (scrollCheck === \"DOWN\" && currentHeight !== maxHeight) {\r\n // scroll이 down이고 맨 아래가 아닐 때\r\n scrollRef.current.height += window.innerHeight; // 상기 동일\r\n scrollToSmoothly(scrollRef.current.height, 500);\r\n }\r\n};\r\n\r\nuseEffect(() => {\r\n smoothScrollHandler();\r\n}, [scrollCheck]); // useScroll()을 통해 구분한 UP/Down이 변경될 때마다 실행\r\n```\r\n\r\n위의 함수를 실행하면 아래와 같이 그나마 정상 작동하는 모습을 볼 수 있다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/cpwxY9/btr5NJNGHOP/34qTHrvGprHknp2jmkpkpK/img.gif)\r\n\r\n## **a 태그를 통한 스크롤 이동**\r\n\r\na 태그의 href 속성을 이용해 간단하게 스크롤을 이동시키는 방법도 있다.\r\n\r\n```javascript\r\n\r\n \r\n 1번박스로 이동 // nextjs에서 Link는 a태그의 역할을\r\n 한다.\r\n 2번박스로 이동 // href에 스크롤을 이동시킬 'ID'를 입력하고\r\n 클릭하면\r\n 3번박스로 이동 // 해당 ID요소로 스크롤을\r\n 이동시킨다.\r\n 4번박스로 이동\r\n 5번박스로 이동\r\n 6번박스로 이동\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n```\r\n\r\n---\r\n\r\n**참조**\r\n\r\n**scroll 메서드**\r\n\r\nhttps://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior\r\n\r\nhttps://developer.mozilla.org/en-US/docs/Web/API/Window/scroll\r\n\r\n**requestAnimationFrame**\r\n\r\nhttps://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame\r\n\r\nhttps://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp\r\n"},{"title":"react component lifecycle","description":"React 함수형 컴포넌트와 Class형 컴포넌트 생명주기","category":"react","date":"2023-03-23","content":"\r\n# **React state와 LifeCycle**\r\n\r\n> 함수형 컴포넌트와 class형 컴포넌트의 LifeCycle이 어떻게 되는지 간단하게 비교해 보는 글이다.\r\n\r\n## 클래스형 컴포넌트와 생명주기 메서드\r\n\r\n### 1. Mount(컴포넌트가 처음 실행될 때)\r\n\r\n- state, context, defalutProps 저장\r\n- componentWillMount - 안전하지 않은 접근\r\n- render\r\n- componentDidMount - DOM 접근 가능\r\n\r\n### 2. Props Update(프롭스가 업데이트될때)\r\n\r\n- componentWillReceiveProps - 사용 종료\r\n- shouldComponentUpdate\r\n- componentWillUpdate - 사용 종료\r\n- render\r\n- componentDidUpdate - DOM 접근 가능\r\n\r\n### 3. State Update (스테이트가 업데이트됐을 때)\r\n\r\n- shouldComponentUpdate\r\n- componentWillUpdate - 사용 종료\r\n- render\r\n- componentDidUpdate - DOM 접근 가능\r\n\r\n**사실상 componentWillReceiveProps와 componentWillUpdate의 사용 종료로 state와 props가 업데이트될 때 동일하게 작동한다.**\r\n\r\n### 4. Unmount (컴포넌트가 제거되는 것)\r\n\r\n- componentWillUnmount\r\n\r\n사용 종료의 기준은 아래와 같다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0qQTJ%2Fbtr5h96VtyB%2FqLpFZAk7aybC21CEWPD9H1%2Fimg.png)\r\n\r\n## 함수형 컴포넌트와 useEffect 훅\r\n\r\n### 컴포넌트의 실행\r\n\r\n- 함수형 컴포넌트 호출\r\n\r\n- 함수형 컴포넌트의 내부에서 실행\r\n\r\n- return()으로 화면에 렌더링\r\n\r\n- 생명주기 메서드 대신 useEffect를 통한 비슷한 처리 가능\r\n\r\n### dependency에 따른 useEffect의 실행\r\n\r\n- deps 값이 없는 경우 : 화면이 렌더링 된 이후 수행이 되며, 리렌더링이 발생할 때마다 다시 실행\r\n\r\n- deps 값이 빈 배열인 경우 : 첫 렌더링 완료 후 1회만 실행\r\n\r\n- deps 값이 존재하는 경우 : 첫 렌더링 완료 후 1회 실행 && deps 값이 변경되었을 경우마다 실행\r\n\r\n```javascript\r\n// dep X\r\nuseEffect(() => {\r\n // effect\r\n return () => {\r\n // cleanup\r\n };\r\n});\r\n\r\n// dep []\r\nuseEffect(() => {\r\n return () => {};\r\n}, []);\r\n\r\n// dep [some dep...]\r\nuseEffect(() => {\r\n return () => {};\r\n}, [dep]);\r\n```\r\n\r\n**[Mounting]** useEffect() - 컴포넌트 렌더링 이후 실행\r\n\r\n- dep 설정에 따라 실행됨\r\n\r\n**[Updating]** useEffect() - 컴포넌트 내에서 변화가 발생했을 경우 실행\r\n\r\n- 부모 컴포넌트의 리렌더링, 부모로부터의 props값 변화, 해당 컴포넌트 내에서 state 변경 등\r\n\r\n**[Unmounting]** useEffect() - 컴포넌트 내에서 DOM을 제거할 때 실행되는 메서드\r\n\r\n- 컴포넌트의 DOM이 제거될 때 수행되며 useEffect 내부의 return 값이 사용됨\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbryvuq%2Fbtr5pFcEsKC%2FIUUp45qfkl3kWGMGsXLoF0%2Fimg.png)\r\n\r\n**위와 같이 컴포넌트를 여닫는 페이지가 있다고 가정했을 때, console을 통해 대략적인 흐름을 확인해 볼 수 있다.**\r\n\r\n### **Class형 컴포넌트**\r\n\r\n**컴포넌트가 처음 실행될 때**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOvMHN%2Fbtr5nGi34Pf%2Fr7cGOBhis0thuYoKgE5Y81%2Fimg.png)\r\n\r\n**state가 변경될 때 (props가 변경될 때와 동일)**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD45Ra%2Fbtr5gtxPbHC%2FBcyreT9Rmknfdukr6PuUMK%2Fimg.png)\r\n\r\n**컴포넌트가 제거될 때**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6mDAw%2Fbtr5qsKSrcJ%2FSzo6Rjkm0liLDy3wCZZ27k%2Fimg.png)\r\n\r\n### **함수형 컴포넌트**\r\n\r\n**컴포넌트가 처음 실행될 때**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3fXKy%2Fbtr5paDZhii%2FxGLf5vByXzPMT1jphHzBkK%2Fimg.png)\r\n\r\n**컴포넌트가 업데이트될 때**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7vKwD%2Fbtr5paYhomj%2F5iPCpcpuRTIKbpr0dFNNO1%2Fimg.png)\r\n\r\n> 콘솔에 출력된 것과 같이, useEffect의 return 이후 부분은 componentWillUnmount와 비슷하지만, 컴포넌트가 다시 렌더링 되기 전마다 다시 실행된다.\r\n\r\n**컴포넌트가 제거될 때**\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5feUj%2Fbtr5sdzGrHx%2F1FTXMaZBnR6FmKQmslJRK1%2Fimg.png)\r\n\r\n컴포넌트가 제거될 때는 componentWillUnmount처럼 useEffect의 return 부분이 실행되는 것을 볼 수 있다.\r\n\r\n사용된 코드\r\n\r\n```javascript\r\n// Lifecycle.jsx\r\n\r\nimport { useState } from \"react\";\r\nimport { FunctionComponent } from \"./FunctionComponent\";\r\nimport { ClassComponent } from \"./ClassComponent\";\r\n\r\nexport const Lifecycle = () => {\r\n const [fnOpen, setFnOpen] = useState(false);\r\n const [classOpen, setClassOpen] = useState(false);\r\n const effectComponentHandler = num => {\r\n if (num) {\r\n setFnOpen(!fnOpen);\r\n } else {\r\n setClassOpen(!classOpen);\r\n }\r\n };\r\n\r\n return (\r\n
\r\n

Fn component / Class component

\r\n \r\n \r\n {fnOpen ? : null}\r\n {classOpen ? : null}\r\n
\r\n );\r\n};\r\n```\r\n\r\n```javascript\r\n// FunctionComponent.jsx\r\n\r\nimport { useState, useEffect } from \"react\";\r\n\r\nexport const FunctionComponent = props => {\r\n console.log(\"컴포넌트 렌더링\", props);\r\n const [count, setCount] = useState(0);\r\n useEffect(() => {\r\n // effect\r\n if (count === 0) {\r\n console.log(`useEffect 첫 등장`);\r\n } else {\r\n console.log(`useEffect 다시 등장 count: ${count}`);\r\n }\r\n return () => {\r\n // cleanup\r\n console.log(\r\n \"useEffect 퇴장 --- componentWillUnmount와 비슷하지만 리렌더링마다 사용됨\",\r\n );\r\n };\r\n }, [count]);\r\n\r\n const plus = () => {\r\n setCount(prev => prev + 1);\r\n };\r\n const minus = () => {\r\n setCount(prev => prev - 1);\r\n };\r\n\r\n return (\r\n
\r\n

함수형 컴포넌트

\r\n {count}\r\n \r\n \r\n

Props 받기

\r\n
props1 = {props.some1}
\r\n
props2 = {props.some2}
\r\n
props3 = {props.some3}
\r\n
\r\n );\r\n};\r\n```\r\n\r\n```javascript\r\n// ClassComponent.jsx\r\n\r\nimport { Component } from \"react\";\r\n\r\nexport class ClassComponent extends Component {\r\n constructor(props) {\r\n super(props);\r\n // Class형 컴포넌트의 state는 무조건 객체 형태여야 한다.\r\n this.state = {\r\n counter: 0,\r\n };\r\n console.log(\r\n \"Mount - 컴포넌트가 처음 실행될 때 state, context, defalutProps 저장\",\r\n props,\r\n );\r\n }\r\n plus = () => {\r\n this.setState(state => ({ counter: state.counter + 1 }));\r\n };\r\n minus = () => {\r\n this.setState(state => ({ counter: state.counter - 1 }));\r\n };\r\n\r\n componentWillMount() {\r\n console.log(\"componentWillMount\");\r\n }\r\n componentDidMount() {\r\n console.log(\"componentDidMount - DOM 접근 가능\");\r\n }\r\n componentDidUpdate() {\r\n console.log(\"componentDidUpdate\");\r\n }\r\n shouldComponentUpdate() {\r\n console.log(\"shouldComponentUpdate - state or props 업데이트\");\r\n return true;\r\n }\r\n componentWillUnmount() {\r\n console.log(\"componentWillUnmount\");\r\n }\r\n\r\n render() {\r\n return (\r\n
\r\n

Class형 컴포넌트

\r\n {this.state.counter}\r\n \r\n \r\n

Props 받기

\r\n
props1 = {this.props.some1}
\r\n
props2 = {this.props.some2}
\r\n
props3 = {this.props.some3}
\r\n
\r\n );\r\n }\r\n}\r\n```\r\n\r\n---\r\n\r\n참조\r\n\r\nhttps://ko.reactjs.org/docs/state-and-lifecycle.html\r\n\r\nhttps://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html\r\n"},{"title":"javascript dynamic import","description":"Dynamic import를 사용해 동적으로 모듈 가져오기","category":"javascript","date":"2023-03-18","content":"\r\n# Dynamic import란?\r\n\r\n> import() 표현식으로 사용하며, 표현식은 모듈을 읽고 해당 모듈이 내보내는 것들을 모두 포함하는 객체를 담은 이행된 Promise를 반환한다. 호출은 어디서나 가능하다.\r\n\r\n# Dynamic import를 사용하는 이유\r\n\r\n- 기존 import문은 정적인 방식으로, 정적으로 가져오는 경우 코드 로드 속도가 느려지고, 가져오는 코드가 필요할 가능성이 적거나 없을 수 있다.\r\n- 정적으로 가져올 때 프로그램의 메모리 사용량이 크게 증가하고 가져오는 코드가 필요할 가능성이 낮다.\r\n- import문에 동적 매개변수를 사용할 수 없다.\r\n\r\n> 즉, 동적으로 import하는 것은 성능 향상과 필요한 경우에 맞춰 사용하는 것에 용이하다.\r\n\r\n## 사용법\r\n\r\n필요한 함수들을 정의하고, 필요한 곳에서 import하여 원하는 방식대로 적용해준다.\r\n\r\n예시 코드\r\n\r\n```javascript\r\n// someFn.js\r\n\r\nexport const greeting = () => {\r\n console.log(\"어서오시고\");\r\n};\r\n\r\nexport const importMe = () => {\r\n console.log(\"다이나믹 임포트를 해주세요\");\r\n};\r\n\r\nexport const easy = () => {\r\n console.log(\"쉽죠?\");\r\n};\r\n\r\nexport const add = (a, b) => console.log(a + b);\r\n```\r\n\r\n```javascript\r\n// use import()\r\nexport const DynamicImport = () => {\r\n // async / await 방식\r\n const loadGreeting = async () => {\r\n const DI = await import(\"./someFn.js\");\r\n DI.greeting();\r\n };\r\n const loadImportMe = async () => {\r\n const DI = await import(\"./someFn.js\");\r\n\r\n DI.importMe();\r\n };\r\n const loadEasy = async () => {\r\n const DI = await import(\"./someFn.js\");\r\n DI.easy();\r\n };\r\n\r\n const loadAddAwait = async (a, b) => {\r\n const DI = await import(\"./someFn.js\");\r\n return DI.add(a, b);\r\n };\r\n // 기본 방식\r\n const loadAdd = (a, b) => {\r\n const DI = import(\"./someFn.js\")\r\n .then(module => {\r\n module.add(a, b);\r\n })\r\n .catch(err => err);\r\n return DI;\r\n };\r\n\r\n return (\r\n
\r\n

DynamicImport component

\r\n \r\n \r\n \r\n \r\n \r\n
\r\n );\r\n};\r\n```\r\n\r\n## Promise를 반환하기 때문에 async/await을 통한 사용도 가능하다.\r\n\r\n위 코드를 통해 생성한 버튼들을 모두 한 번씩 클릭해주면 아래와 같이 사용되는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyOVoT%2Fbtr4v801voG%2FmX38iUM5zpLCGKZ5nkyWYK%2Fimg.png)\r\n\r\n> 참고사항 : Dynamic import는 일반 스크립트에서도 동작하기 때문에 script type=\"module\"이 없어도 된다.\r\n\r\n### 주의사항 : import()는 함수 호출과 문법이 유사해 보이지만 함수 호출이 아니다. super()처럼 괄호를 사용하는 특별한 문법 중 하나이다. 따라서 import를 변수에 복사하거나 call/apply 등의 사용은 불가능하다.\r\n\r\n---\r\n\r\n참조\r\n\r\nMDN : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import\r\n\r\nModern js : https://ko.javascript.info/modules-dynamic-imports\r\n"},{"title":"javascript jest test code","description":"jest를 통한 테스트코드 사용","category":"javascript","date":"2023-03-18","content":"\r\n# jest란?\r\n\r\n> 단순성에 중점을 둔 javascript 테스트 프레임워크로, babel, Typescript, node, React, Angular, Vue 등을 사용하는 프로젝트에서 작동한다.\r\n\r\n본 포스팅은 React 환경을 기준으로 작성되었다.\r\n\r\n## 1. jest 설치\r\n\r\n```bash\r\nnpm install --save-dev jest @babel/preset-env\r\n# or\r\nyarn add --dev jest @babel/preset-env\r\n```\r\n\r\nnode.js 환경에서 import 등의 ES6 문법을 사용하기 위해 @babel/preset-env도 함께 설치해준다.\r\n\r\n## 2. 루트 경로에 babel.config.js 파일 생성\r\n\r\n```javascript\r\nmodule.exports = {\r\n presets: [\r\n \"@babel/preset-env\",\r\n // typescript를 사용하는 경우 yarn add --dev @babel/preset-typescript 설치 후\r\n // 아래와 같이 preset을 추가해준다.\r\n // \"@babel/preset-typescript\"\r\n ],\r\n};\r\n```\r\n\r\nbabel config 파일까지 생성이 완료됐다면 test 파일을 사용할 준비가 다 된 것이다.\r\n\r\n## 3. jest가 인식하는 test file 이름\r\n\r\n- {filename}.test.js\r\n- {filename}.spec.js\r\n\r\n## 4. 테스트 실행\r\n\r\njest 커맨드만으로도 테스트 코드를 진행할 수 있지만, 다른 플래그와 함께 사용할 수 있다.\r\n\r\n```bash\r\n--coverage # 프로젝트의 테스트 커버리지를 함께 나타내준다.\r\n--watch # 대상 코드나 테스트 코드에 변경이 생기면 테스트를 다시 실행한다.\r\n\r\n# ex) yarn jest --watch --coverage\r\n```\r\n\r\njest만 실행한 터미널\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmEHBz%2Fbtr4t9fB6IJ%2Fl1JqzgbkjoBxDREUdoSgzK%2Fimg.png)\r\n\r\n--coverage와 함께 실행한 터미널\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPTj6f%2Fbtr4vjvmeEt%2Ft5NIYNk6kzRJ4PQ1IQA48k%2Fimg.png)\r\n\r\n> 참고 : jest는 테스트 코드 내에서 오류가 발생할 때만 테스트를 fail시키기 때문에 test(\"\",()=>{})와 같이 테스트 코드가 비어있더라도 테스트를 통과할 수 있다.\r\n\r\n## 5. 테스트 코드 패턴\r\n\r\n### AAA 패턴 (Arrange - Act - Assert)\r\n\r\n- A 조건에서 B를 실행했을때 예상한 결과가 C가 나오는가? 라는 흐름의 패턴이다.\r\n\r\n예제\r\n\r\n```javascript\r\n// 기존 함수\r\nexport const add = (a, b) => {\r\n return a + b;\r\n};\r\n\r\n// somefn.spec.js - 테스트코드 함수\r\n\r\nimport { add } from \"./someFn\";\r\n\r\ntest(\"add 함수 테스트: result = a+b\", () => {\r\n // 조건\r\n const a = 2;\r\n const b = 3;\r\n const result = 5;\r\n\r\n // 실행\r\n const actResult = add(a, b);\r\n\r\n // 평가\r\n expect(actResult).toBe(result);\r\n});\r\n```\r\n\r\n평가 부분의 코드는 .toBe()라는 matcher 함수를 통해 actResult를 실행했을 때 result가 나올 것이라는 기대를 갖고 코드를 평가하게 된다.\r\n\r\n아래와 같이 여러 함수를 한 테스트 코드 안에 넣을 수도 있지만, 리팩토링 시 불편한 경험을 할 수 있기 때문에 상황에 맞게 사용해주는 것이 좋을 것 같다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckHjWD%2Fbtr4t5Eptqn%2FccX4fWxWW6GZ9edDMtjdAK%2Fimg.png)\r\n\r\n주요 matcher 함수\r\n\r\n- toBe() : 원시형 데이터 비교\r\n- toEqual() : 참조형 데이터를 깊은 비교를 통해 비교\r\n- toBeTruthy() : 값이 참인지 검사\r\n- toContain() : 특정 요소가 배열 안에 존재하는지 검사\r\n- not : 다른 matcher 함수 앞에 체이닝해 사용하며, 해당 값의 반대값이 참인지 검사 (not은 함수가 아닌 property)\r\n\r\n공식 문서에서 더 자세하게 확인해볼 수 있다. (https://jestjs.io/docs/using-matchers)\r\n\r\n## 6. 테스트 코드를 통해 할 수 있는 것\r\n\r\n- 내가 작성한 함수들이 내가 원하는대로 작동하는지 확인할 수 있다.\r\n- 테스트 코드 작성을 통해 TDD 방식의 개발 방법론을 적용할 수 있다.\r\n\r\n## TDD란?\r\n\r\n> TDD는 테스트 주도 개발(Test Driven Development)을 뜻하며, 테스트 케이스를 우선적으로 설계해 개발을 진행하는 방법론이다.\r\n\r\n### 1. 테스트 케이스를 우선적으로 설계하고,\r\n\r\n### 2. 함수를 테스트 케이스에 맞춰 제작하며,\r\n\r\n### 3. 로직을 개선하며 1~3을 반복한다.\r\n\r\n즉, 실패-성공-리팩토링의 반복으로 볼 수 있다.\r\n\r\nTDD의 예시\r\n\r\nhttps://velog.io/@velopert/TDD%EC%9D%98-%EC%86%8C%EA%B0%9C\r\n\r\n---\r\n\r\n참조 : https://jestjs.io/\r\n"},{"title":"nextjs link userouter","description":"Nextjs Link와 useRouter의 차이","category":"nextjs","date":"2023-03-18","content":"\r\n# Link / useRouter 사용법\r\n\r\n```javascript\r\n// Link\r\nimport Link from \"next/link\";\r\n\r\nexport const compo1 = () => {\r\n return (\r\n \r\n
some element
\r\n \r\n );\r\n};\r\n\r\n// useRouter\r\nimport { useRouter } from \"next/router\";\r\n\r\nexport const compo2 = () => {\r\n const router = useRouter();\r\n\r\n const routeHandler = () => {\r\n router.push(url);\r\n };\r\n\r\n return (\r\n
\r\n some element\r\n
\r\n );\r\n};\r\n```\r\n\r\n### Nextjs에서 페이지를 전환하기 위해 사용하는 방법으로, Link를 이용하거나 useRouter를 이용해 router.push()와 같이 사용하여 전환해줄 수 있는데, 비슷해 보이는 두 가지 방법의 차이를 간단히 정리해본다.\r\n\r\n## Link의 특징\r\n\r\n- Link는 Client-side navigation으로, javascript로 페이지 전환이 이뤄진다.\r\n- 기본 navigation보다 빠르며 SPA(Single Page Application)의 특성을 유지한다.\r\n\r\n## useRouter의 특징\r\n\r\n- react-router-dom의 history.push()와 유사하다.\r\n- 크롤러가 링크를 감지하지 못해 SEO가 좋지 않을 수 있다.\r\n- 외부 URL을 사용할 경우 window.location 혹은 a 태그를 사용해야 한다.\r\n\r\n또한 Link는 클릭 시 바로 페이지가 전환되지만, useRouter는 로직을 처리한 후 원하는 시점에 전환이 가능하다.\r\n\r\n## 결론\r\n\r\n> router.push()는 onClick에 사용되는 행동(action)이기 때문에 Link 태그보다 검색에 불리하지만, 로직의 처리에 따라 활용도가 높다.\r\n\r\n> Nextjs의 장점인 SEO(Search Engine Optimization - 검색 엔진 최적화)를 원한다면 Link를 사용하는 것이 더 유리하다.\r\n\r\n---\r\n\r\n참조\r\n\r\nhttps://stackoverflow.com/questions/65086108/next-js-link-vs-router-push-vs-a-tag\r\n\r\nhttps://nextjs.org/docs/api-reference/next/link\r\n\r\nhttps://nextjs.org/docs/api-reference/next/router\r\n"},{"title":"nextjs marked webpack imported module 7 default","description":"WEBPACK_IMPORTED_MODULE_7_default is not a function 에러 해결","category":"nextjs","date":"2023-03-18","content":"\r\n# 에러 해결 방법\r\n\r\n나의 경우 SSR 데이터를 contextAPI를 통해 패치하려 했으나, 정상적으로 받아오지 못할때 발생한 오류였다.\r\n\r\n로컬 실행 종료 후 재실행하니 정상적으로 데이터를 받아올 수 있었다.\r\n\r\n다소 허탈한 해결방안이었지만, SSR을 다루는 페이지의 데이터가 정상적으로 받아와지지 않는 경우, 코드 작성 후 프로그램을 다시 실행해보자.\r\n\r\n---\r\n\r\n참조 : https://github.com/vercel/next.js/issues/18090\r\n"},{"title":"react memo","description":"React.memo() 를 통한 컴포넌트 최적화","category":"react","date":"2023-03-16","content":"\r\n> 유저에게 UI를 빠르게 제공하기 위해서는 컴포넌트의 렌더링을 최소화해 성능을 향상시킬 필요가 있다. 이를 위해 React.memo()에 대한 간단한 사용법을 기록해본다.\r\n\r\n# 1. React.memo()\r\n\r\n컴포넌트가 React.memo()로 래핑될 때, React는 컴포넌트를 렌더링하고 결과를 Memoizing한다.\r\n\r\n그리고 다음 렌더링이 일어날 때 props가 값다면, React는 Memoizing된 내용을 재사용한다.\r\n\r\n## 사용법\r\n\r\n```javascript\r\nimport React from \"react\";\r\n\r\nexport const MemoTest = ({ someProps }) => {\r\n console.log(\"Memo test, props 바뀔때만 콘솔 작동\");\r\n return (\r\n
\r\n

MemoTest 컴포넌트

\r\n

props가 바뀔때만 렌더링

\r\n count와 연동된 props: {someProps}\r\n
\r\n );\r\n};\r\n\r\n// 다른 컴포넌트에서 불러올 때, 해당 컴포넌트를 import 해주면 된다.\r\nexport const MemoTestComponent = React.memo(MemoTest);\r\n```\r\n\r\n## 사용 예시\r\n\r\n```javascript\r\nexport const TestApp = () => {\r\n const values = useContextValue();\r\n const update = useContextUpdate();\r\n const [count, setCount] = useState(0);\r\n console.log(\"최상위 컴포넌트 : 렌더링 될때마다 콘솔 작동\");\r\n\r\n const contextHandler = () => {\r\n values === \"someData\" ? update(\"dataSome\") : update(\"someData\");\r\n };\r\n\r\n return (\r\n
\r\n \r\n \r\n {\r\n setCount(pre => pre + 1);\r\n }}\r\n >\r\n +count버튼\r\n \r\n {\r\n setCount(pre => pre - 1);\r\n }}\r\n >\r\n -count버튼\r\n \r\n
\r\n

context value

\r\n value : {values}\r\n \r\n
\r\n
\r\n );\r\n};\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVQkSV%2Fbtr37vCBXzm%2FVXiqfYdD4H2FTcw3tGVFzk%2Fimg.png)\r\n\r\n위 이미지는 TestApp 코드의 초기 화면이다.\r\n\r\n모든 컴포넌트가 첫 렌더링을 거쳐 각각 콘솔을 찍어내고 있다.\r\n\r\n### TestApp의 count state는 MemoTest 컴포넌트에 props로 전달해주고 있기 때문에 React.memo()의 효과는 사용되지 않고, 일반 컴포넌트와 마찬가지로 TestApp count state가 변경될 때마다 렌더링되는 것을 볼 수 있다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/Hl6oT/btr4gSJ05tT/kipvw3VkehYfnxf2Pbq780/img.gif)\r\n\r\n### 하지만 contextAPI처럼 MemoTest의 props와 연관되지 않은 다른 방식을 통해 통해 리렌더링을 발생시키는 경우라면, memoTest 컴포넌트의 props가 변경되지 않기 때문에 MemoTest는 렌더링을 발생시키지 않는다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/cmaheq/btr36GRW8J1/uhzNP5d6ymqDxVDGFK4Ay1/img.gif)\r\n\r\n> contextAPI의 기본 사용법 : https://lee-yo-han.github.io/react/react-context-api\r\n\r\n---\r\n\r\n## 1-1 - props 동등 비교 커스터마이징\r\n\r\nReact.memo()는 props 혹은 props의 객체를 비교할 때 얕은 비교를 하기 때문에, 비교 방식을 수정하고 싶다면 React.memo() 두 번째 매개변수로 비교함수를 만들어 수동으로 연산 후 넘겨주면 된다.\r\n\r\n```javascript\r\nimport React from \"react\";\r\n\r\nexport const MemoTest = ({ someProps }) => {\r\n console.log(\"Memo test, props 바뀔때만 콘솔 작동\");\r\n return (\r\n
\r\n

MemoTest 컴포넌트

\r\n

props가 바뀔때만 렌더링

\r\n count와 연동된 props: {someProps}\r\n
\r\n );\r\n};\r\n\r\nconst propsAreEqual = (prev, next) => {\r\n return (\r\n prev.someProps === next.someProps\r\n // 다른 props들이 있는 경우 &&을 통해 연산 추가\r\n );\r\n};\r\n\r\nexport const MemoTestComponent = React.memo(MemoTest, propsAreEqual);\r\n```\r\n\r\n# 2. React.memo()는 언제 사용해야 하는가?\r\n\r\n### - 함수형 컴포넌트가 같은 props로 자주 렌더링 될거라 예상될 경우\r\n\r\nprops가 변경되지 않는 경우에도 상위 컴포넌트의 지속적인 렌더링에 의해 하위 컴포넌트의 불필요한 렌더링이 예상되는 경우라면 React.memo()를 통해 오직 props의 변화에만 반응시켜주는 것이 효율적이다.\r\n\r\n### - 무겁고 비용이 큰 연산이 있는 경우\r\n\r\n한 번 렌더링 될 때 큰 연산이 발생한다고 했을때 불필요한 렌더링이 일어난 경우 연산에 사용되는 비용이 낭비될 수 있기 때문에 이러한 경우 또한 React.memo()를 통한 memoization이 적절하다고 볼 수 있다.\r\n\r\n# 3. React.memo()를 언제 사용하지 말아야 하는가?\r\n\r\n### - 위에 언급한 상황과 맞지 않는 경우 React.memo()를 사용할 필요가 없을 가능성이 높다.\r\n\r\n경험적으로 ㅅ어능적인 이점을 얻지 못한다면 memoization을 사용하지 않는 것이 좋다. 성능 관련 변경이 잘못 적용된다면 성능이 오히려 악화될 수 있다.\r\n\r\n### - 기술적으로는 가능하지만 클래스 기반의 컴포넌트를 React.memo()로 래핑하는 것은 적절하지 않다.\r\n\r\n클래스 기반의 컴포넌트에서 memoization이 필요하다면, pureComponent를 확장하여 사용하거나, shouldComponentUpdate() 메서드를 구현하는 것이 적절하다.\r\n\r\n### - props가 자주 바뀌는 경우\r\n\r\nprops가 자주 바뀌는 경우도 React.memo()로 래핑하더라도 이전 props와 다음 props를 비교 함수를 통해 비교할때 거의 false를 반환할 것이기 때문에 props가 자주 바뀌는 경우도 React.memo()의 사용이 적절하지 않다.\r\n\r\n## 결론\r\n\r\n> React.memo는 memoization의 장점을 얻게 해주는 좋은 도구이지만, 렌더링 방지를 위해 memoization에만 의존하면 안된다.\r\n\r\n---\r\n\r\n## 참조\r\n\r\nhttps://dmitripavlutin.com/use-react-memo-wisely/\r\n\r\nhttps://ui.toast.com/weekly-pick/ko_20190731\r\n"},{"title":"react usecallback","description":"useCallback을 이용한 컴포넌트 최적화하기","category":"react","date":"2023-03-16","content":"\r\n# useCallback이란?\r\n\r\n> 특정 함수를 새로 만들지 않고 재사용하고 싶을때 사용하는 함수 메모이제이션용 React hook이다.\r\n\r\n```javascript\r\nconst memoFn = useCallback(() => {}, [dep]);\r\n```\r\n\r\n첫번째 인자의 함수를 두번째 인자의 종속성 배열 내의 값이 변경될 때까지 저장하여 재사용할 수 있도록 해준다.\r\n\r\njavascript에서 함수(function () {} or () => {})는 객체 리터럴( {...} )이 항상 새 객체를 생성하는 것과 유사하게 항상 다른 함수를 생성한다. 그렇다면 함수를 props 로 내려준다고 하면 하위 컴포넌트는 props가 변경되었다고 인식하게 된다.\r\n\r\n그렇게되면 React.memo를 이용한 최적화가 원하는대로 작동하지 않을 수 있기 때문에 이러한 점을 useCallback이 메꿔줄 수 있게 된다.\r\n\r\n> 종속성 배열이 없는 경우 매번 새 함수를 반환하기 때문에 항상 dependencises 배열을 포함해줘야 한다. (특정 상황에서 빈 배열 [] 은 있을 수 있지만, 배열 자체가 없으면 의미가 없어진다.)\r\n\r\n## 사용예\r\n\r\n```javascript\r\nconst add = useCallback(() => x + y, [x, y]);\r\n```\r\n\r\n# useCallback과 React.memo를 통한 최적화\r\n\r\n```javascript\r\n// 최상단 (1번) 컴포넌트\r\nimport { useState } from \"react\";\r\nimport { UseCallbackChildren } from \"./UseCallbackChildren\";\r\n\r\nexport const UseCallbackTest = () => {\r\n const [isDark, setIsDark] = useState(false);\r\n return (\r\n <>\r\n \r\n
\r\n \r\n \r\n );\r\n};\r\n\r\n// 중간 (2번) 컴포넌트\r\nimport React, { useCallback } from \"react\";\r\nimport { CallbackForm } from \"./UseCallbackForn\";\r\n\r\nexport const UseCallbackChildren = ({ referrer, productId, theme }) => {\r\n const post = (url, data) => {\r\n console.log(\"POST /\" + url);\r\n console.log(data);\r\n };\r\n\r\n const handleSubmit = useCallback(\r\n orderDetails => {\r\n post(\"/product/\" + productId + \"/buy\", {\r\n referrer,\r\n orderDetails,\r\n });\r\n },\r\n [productId, referrer],\r\n );\r\n\r\n return (\r\n \r\n \r\n
\r\n );\r\n};\r\n\r\n// 하위 (3번) 컴포넌트\r\nimport React, { useState } from \"react\";\r\n\r\nexport const UseCallbackForm = ({ onSubmit }) => {\r\n const [count, setCount] = useState(1);\r\n\r\n console.log(\"강제로 느리게\");\r\n let startTime = performance.now();\r\n while (performance.now() - startTime < 500) {\r\n // Do nothing for 500 ms to emulate extremely slow code\r\n }\r\n\r\n const handleSubmit = e => {\r\n e.preventDefault();\r\n const formData = new FormData(e.target);\r\n const orderDetails = {\r\n ...Object.fromEntries(formData),\r\n count,\r\n };\r\n onSubmit(orderDetails);\r\n };\r\n\r\n return (\r\n \r\n

\r\n \r\n Note: ShippingForm is artificially slowed down!\r\n \r\n

\r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport const CallbackForm = React.memo(UseCallbackForm);\r\n```\r\n\r\n위 코드를 보면, 최상단(1)에 있는 다크모드는 중간(2) 컴포넌트까지만 전달해주고, 중간(2) 컴포넌트는 하위(3) 컴포넌트에 useCallback 함수만 전달해주고 있다.\r\n\r\nuseCallback은 함수를 캐시해 재사용할 수 있도록 도와주기 때문에 종속성이 변하지 않으면 계속 재사용할 수 있다. 즉, 동일한 props로써 함수를 하위(3) 컴포넌트에 내려주고 있기 때문에 하위 컴포넌트의 렌더링에 영향이 없다.\r\n\r\n이 상태로 코드를 작동시키면 하위(3) 컴포넌트에 있는 count를 동작할때마다 느려지겠지만, 다크모드와 submit 이벤트에는 영향을 주지 않는다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/bEYsKw/btr4gSLc8BG/hJKpnBKbFziku3fjI6hphK/img.gif)\r\n\r\n만약 하위(3) 컴포넌트의 React.memo()를 없애게 되면 submit 이벤트에는 영향이 없지만, 최상위(1) 컴포넌트의 다크모드 state 변경에 의해 하위(3) 컴포넌트의 렌더링이 일어나게 되어 다크모드와 count의 동작이 느려지는걸 볼 수 있다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/mfxA0/btr4iTijPZD/XNzKmFrScoUZ6qSyiCMV2K/img.gif)\r\n\r\n자세히 보면 콘솔이 먼저 찍히고 0.5초정도 뒤에 동작하는 것을 볼 수 있다.\r\n\r\n# useMemo와의 차이점\r\n\r\n> useMemo는 함수의 호출 결과를 캐시하고, useCallback은 함수 자체는 캐시한다. 그러므로 useCallback은 useMemo와 달리 제공한 함수를 호출하지 않는다. 대신 사용자가 제공한 함수를 캐시하기 때문에 불필요한 렌더링 없이 함수를 전달할 수 있다.\r\n\r\n## useMemo와 useCallback의 동시 사용\r\n\r\n```javascript\r\nfunction useCallback(fn, dependencies) {\r\n return useMemo(() => fn, dependencies);\r\n}\r\n```\r\n\r\n## useCallback이 유용한 경우\r\n\r\n- useCallback을 통해 props로 하위 컴포넌트에 넘겨줄때\r\n- useEffect 등 다른 hook의 종속성으로 이용할때\r\n- 종속성 변화가 적고 재사용이 용이할때\r\n\r\n## useCallback 남용 시 문제점\r\n\r\n> 모든 함수를 useCallback으로 감싸게 되면 컴포넌트가 리렌더 될 때마다 모든 함수가 다시 재생성될 필요가 있는지 검사하는 연산이 수행된다. 따라서 보통은 특정 함수가 props로 전달되어 불필요한 컴포넌트 리렌더를 유발할 때에만 적용하는 것이 좋다.\r\n\r\n### memo, useMemo, useCallback 등 어떤 Memoization이나 부적절하게 사용할 경우 오히려 성능이 떨어질 수 있기 때문에 상황에 맞게 적절하게 사용하는 것이 좋은 것 같다.\r\n\r\n---\r\n\r\n참조: https://beta.reactjs.org/reference/react/useCallback\r\n"},{"title":"react usememo","description":"useMemo를 이용한 컴포넌트 최적화하기","category":"react","date":"2023-03-16","content":"\r\n# useMemo란?\r\n\r\n> useMemo는 React.memo(), useCallback과 같이 컴포넌트를 최적화하기 위한 훅 중 하나이다.\r\n\r\n# useMemo를 사용하는 이유\r\n\r\nReact의 컴포넌트는 부모 컴포넌트가 렌더링되거나, 자신의 state변경, 상위 컴포넌트에서 내려받는 props가 변경될 때마다 다시 렌더링된다. 그런데 내려받는 props나 state가 여러 가지일때 한 개 props의 변경으로 모든 값이 다시 계산되는 경우 불필요한 비용이 누적되게 된다.\r\n\r\nuseMemo는 이러한 현상을 효율적으로 전환하기 위한 hook이다.\r\n\r\n## useMemo를 적용하기 전\r\n\r\n```javascript\r\n// 상위 컴포넌트\r\nimport { useState } from \"react\";\r\nimport { UseMemoChildren } from \"./UseMemoChildren\";\r\n\r\nexport const UseMemoTest = () => {\r\n const [number, setNumber] = useState(0);\r\n const [text, setText] = useState(\"\");\r\n\r\n const changeNumber = e => {\r\n setNumber(e.target.value);\r\n };\r\n const changeText = e => {\r\n setText(e.target.value);\r\n };\r\n\r\n return (\r\n
\r\n

UseMemoText

\r\n
\r\n

number

\r\n \r\n
\r\n
\r\n

text

\r\n \r\n
\r\n \r\n
\r\n );\r\n};\r\n\r\n// 하위 컴포넌트\r\nexport const UseMemoChildren = ({ number, text }) => {\r\n const getNumber = number => {\r\n console.log(\"숫자 변경\");\r\n return number;\r\n };\r\n\r\n const getText = text => {\r\n console.log(\"글자 변경\");\r\n return text;\r\n };\r\n\r\n const showNumber = getNumber(number);\r\n\r\n const showText = getText(text);\r\n\r\n return (\r\n
\r\n

Memo Child Component

\r\n

number: {showNumber}

\r\n

text: {showText}

\r\n
\r\n );\r\n};\r\n```\r\n\r\n상위 컴포넌트는 각 input 값이 변경될 때마다 그 값을 저장해 하위 컴포넌트로 내려준다.\r\n\r\n하위 컴포넌트는 내려받은 props를 통해 화면에 노출시켜줄 값을 return해주는 역할을 한다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/9hqzj/btr4hQeyTmU/PzYhcCiwuZq08zOkhBqeWk/img.gif)\r\n\r\n이 상태에서 input을 업데이트하면 숫자를 바꾸든 텍스트를 바꾸든 getNumber 함수와 getText 함수 둘 다 다시 연산되는 것을 볼 수 있다.\r\n\r\n이런 경우 변경된 props에 대해서만 다시 연산될 수 있도록 useMemo를 적용해볼 수 있다.\r\n\r\n## useMemo 사용법\r\n\r\n```javascript\r\nconst cachedValue = useMemo(calculateValue, dependencies);\r\n```\r\n\r\n### 적용 예시\r\n\r\n```javascript\r\n// 하위 컴포넌트\r\n\r\nimport { useMemo } from \"react\";\r\n\r\nexport const UseMemoChildren = ({ number, text }) => {\r\n // console.log(\"useMemoChildren start\");\r\n const getNumber = number => {\r\n console.log(\"숫자 변경\");\r\n return number;\r\n };\r\n\r\n const getText = text => {\r\n console.log(\"글자 변경\");\r\n return text;\r\n };\r\n\r\n const showNumber = useMemo(() => {\r\n return getNumber(number);\r\n }, [number]);\r\n\r\n const showText = useMemo(() => {\r\n return getText(text);\r\n }, [text]);\r\n\r\n return (\r\n
\r\n

Memo Child Component

\r\n

number: {showNumber}

\r\n

text: {showText}

\r\n
\r\n );\r\n};\r\n```\r\n\r\n위와 같이 useMemo를 적용하고 나면 다음과 같이 useMemo 적용 이후 변경된 props에 대한 연산만 처리해주는 것을 볼 수 있다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/4Vcyh/btr4hPz1xwm/0zNRNCdyJKwpsWk2LU73KK/img.gif)\r\n\r\n> useMemo 마지막의 dependencies [ ] 배열은 필수적으로 어떤 값을 변경에 반응할 것인지 포함시켜줘야 한다.\r\n\r\n# 연산 비용이 비싸다는 기준은?\r\n\r\nReact 공식 사이트에서 권장하는 useMemo 적용 기준은 연산에 소요된 시간이 1ms 이상일 경우로 안내하고 있다.\r\n\r\nconsole.time/timeEnd 등을 통해 useMemo를 적용하고자 하는 함수의 총 연산시간을 도출해볼 수 있다.\r\n\r\n```javascript\r\nconst getText = text => {\r\n console.time(\"memo\");\r\n console.log(\"글자 변경\");\r\n console.timeEnd(\"memo\");\r\n return text;\r\n};\r\n```\r\n\r\n위와 같이 코드 작성 후 text를 변경해보면 다음과 같은 콘솔을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8mGtS%2Fbtr4iU19jHE%2FH0huW2SytbAtalR3urepr0%2Fimg.png)\r\n\r\n> 입력하는 계산이 눈에 띄게 느리고, 종속성이 거의 변경되지 않는 경우에는 useMemo를 이용한 최적화가 적합하지만, 컴포넌트 구조 변경과 상태 관리의 최소화, state를 업데이트하는 Effect의 불필요한 종속성을 제거하거나 특정 함수들을 별도로 관리하는 방법이 useMemo보다 적합할 수 있다.\r\n\r\n> 실제로 React의 성능 문제 대부분은 Effect에서 발생하는 업데이트 체인으로 인해 발생한다고 한다.\r\n\r\n---\r\n\r\n참조 : https://beta.reactjs.org/reference/react/useMemo\r\n"},{"title":"react context api","description":"ContextAPI 사용하기","category":"react","date":"2023-03-15","content":"\r\n# ContextAPI\r\n\r\n> 간단한 데이터의 변화를 props drilling 없이 전역적으로 관리해주기 위해 ContextAPI를 사용해볼 수 있다.\r\n\r\n## 사용법\r\n\r\n## 1. context component 생성\r\n\r\n```javascript\r\n// context.jsx\r\nimport { createContext, useContext, useState } from \"react\";\r\n\r\n// context 생성\r\nconst AnyContextValue = createContext();\r\nconst AnyContextUpdate = createContext();\r\n\r\n// useContext 생성\r\nexport const useContextValue = () => {\r\n const context = useContext(AnyContextValue);\r\n return context;\r\n};\r\nexport const useContextUpdate = () => {\r\n const update = useContext(AnyContextUpdate);\r\n return update;\r\n};\r\n\r\n// context Component 생성\r\n// children props를 Contextname.provider로 감싸준다.\r\nexport const SomeContext = ({ children }) => {\r\n const [someData, setSomeData] = useState(\"someData\");\r\n return (\r\n \r\n \r\n {children}\r\n \r\n \r\n );\r\n};\r\n```\r\n\r\n## 2. context import\r\n\r\n```javascript\r\nimport { TestApp } from \"./components/blogtest/TestApp\";\r\nimport { SomeContext } from \"./components/context/RenderingContext\";\r\n\r\n// import된 Context를 적용하고자 하는 컴포넌트들의 최상위에 감싸준다.\r\nfunction App() {\r\n return (\r\n \r\n \r\n \r\n );\r\n}\r\n\r\nexport default App;\r\n```\r\n\r\n## 3. context 사용\r\n\r\n```javascript\r\nimport { useState } from \"react\";\r\n// 생성한 useContext import\r\nimport { useContextUpdate, useContextValue } from \"../context/RenderingContext\";\r\n\r\nexport const ChildrenComponent = () => {\r\n const values = useContextValue();\r\n const update = useContextUpdate();\r\n\r\n const contextHandler = () => {\r\n values === \"someData\" ? update(\"dataSome\") : update(\"someData\");\r\n };\r\n\r\n return (\r\n
\r\n

context value

\r\n value : {values}\r\n \r\n
\r\n );\r\n};\r\n```\r\n\r\n위와 같이 설정이 완료된 후 정상적으로 contextAPI를 사용할 수 있다.\r\n\r\n![image](https://blog.kakaocdn.net/dn/d0hnZa/btr35w9FvGX/zDcTOjtXxg23vMKk6LxaM0/img.gif)\r\n\r\n> contextAPI는 간단하게 상태를 관리해줄 수 있지만, provider를 전역으로 감쌌을 때 컴포넌트들이 전반적으로 렌더링이 일어날 수 있기 때문에 상황과 구조에 맞게 적절하게 사용해주는 것이 좋다.\r\n\r\n---\r\n\r\n## 참고 : typescript에서의 contextAPI 사용\r\n\r\n### TypeScript에서는 context 초기값 등의 설정이 필요하다.\r\n\r\n```typescript\r\n// context component\r\n\r\nimport { createContext, useContext, useState } from \"react\";\r\nimport { PostType } from \"@/types/pages\";\r\nimport { ProviderProps, ContextType } from \".\";\r\n\r\n// context 초기화\r\nconst initialContext = {\r\n posts: [],\r\n setPosts: () => {},\r\n};\r\n\r\n// context 생성\r\nconst PostContextValue = createContext(initialContext.posts);\r\nconst PostContextUpdate = createContext(initialContext.setPosts);\r\n\r\n// useContext 생성\r\nexport const useMdContextValue = () => {\r\n const context = useContext(PostContextValue);\r\n return context;\r\n};\r\nexport const useMdContextUpdate = () => {\r\n const update = useContext(PostContextUpdate);\r\n return update;\r\n};\r\n\r\n// context component 생성\r\nexport const MdContext = ({ children }: ProviderProps) => {\r\n const [posts, setPosts] = useState([]);\r\n\r\n return (\r\n \r\n \r\n {children}\r\n \r\n \r\n );\r\n};\r\n\r\n// 다른 컴포넌트에서의 사용\r\nimport { useEffect } from \"react\";\r\nimport { useMdContextUpdate } from \"@/context/mdContext\";\r\n\r\nexport default function Home({ posts }: { posts: PostType[] }) {\r\n const update = useMdContextUpdate();\r\n\r\n useEffect(() => {\r\n if (posts) {\r\n update(posts);\r\n }\r\n }, [posts, update]);\r\n```\r\n"},{"title":"github git blog utterances comment","description":"utterances를 이용한 Nextjs Github 블로그 댓글 기능 구현","category":"github","date":"2023-03-14","content":"\r\n# utterances란?\r\n\r\n> Github repository의 이슈 기능을 활용해 내 웹사이트에 댓글 기능을 추가할 수 있는 도구로, 별도의 백엔드 구성을 하지 않고 댓글들을 관리할 수 있다는 장점이 있다.\r\n\r\n많이 비교되는 disqus라는 툴도 있다. 하지만, disqus는 댓글 작성을 누구나 할 수 있다는 장점이 있는 반면에 광고가 많다는 단점이 있다.\r\n그에 비해 utterances는 Github 계정 소유자만 댓글을 작성할 수 있지만, 별도의 광고도 없고 댓글 작성 시 Markdown을 지원한다는 특징이 있다.\r\n\r\n나는 utterances를 Github blog에 사용할 생각이기에 굳이 utterances의 장점을 버리고 disqus를 사용할 이유가 없었다.\r\n\r\n# 사용방법\r\n\r\n## 1. repository 생성\r\n\r\npublic으로 설정된 새 레포가 필요하다.\r\n\r\nGithub blog repo에 연동해도 상관은 없지만 댓글만을 위한 저장소를 만들어 놓는게 관리가 훨씬 쉬울 것 같아 새 repo를 생성해줬다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbq4F8z%2Fbtr34VuvScp%2FwmTQX2iIjKtMjlowowHw80%2Fimg.png)\r\n\r\n저장소 이름은 상관 없다.\r\n\r\n## 2. utterances 설치\r\n\r\nGithub app에서 설치할 수 있다.\r\n\r\nhttps://github.com/apps/utterances\r\n\r\n## 3. utterances access 설정\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPS87y%2Fbtr35bYlHCH%2FhE9akcKFM7qYE6yAfvKD4k%2Fimg.png)\r\n\r\napp이 설치되면 Configure를 통해 어떤 저장소에 연동해줄지 설정할 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F55bSC%2Fbtr37BBZd9Y%2FfSwpH2VCQKwXKpQkXAbO00%2Fimg.png)\r\n\r\n나는 선택한 저장소에만 연동되게 설정했다.\r\n\r\n## 4. utterances.json 생성\r\n\r\n```javascript\r\n{\r\n \"origins\": [\"https://{my url}\"]\r\n}\r\n```\r\n\r\n위와 같이 자신이 댓글 기능을 사용할 사이트를 utterances.json에 입력해준다.\r\n\r\n## 5. script를 블로그 repo code에 복사\r\n\r\nhttps://utteranc.es/?installation_id=35005881&setup_action=install\r\n\r\n해당 사이트에 들어가면 어떤 방식으로 script를 구성할지 설정할 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo9X4N%2Fbtr34VVyI7q%2F2PbuKqgkmKDtfogCNj7Nx0%2Fimg.png)\r\n\r\n> Issue mapping같은 경우, 댓글과 포스팅을 어떻게 연동할지 설정하는 것인데, 통상적으로 pathname이나 URL을 많이 사용하지만 본인 프로젝트의 상황에 맞춰 설정해주는 것이 가장 좋다.\r\n\r\n> 또한 한글 파일명을 사용해 URL이나 pathname을 생성하는 경우, 유니코드 인식 문제로 비정상적인 URL이 생성될 수 있기 때문에 설정에 유의해야 한다.\r\n\r\n```javascript\r\n// 복사할 스크립트\r\n\r\n```\r\n\r\n테마 색상도 다양하기 때문에 utterances 사이트에서 원하는 색상을 골라가며 설정해줘도 괜찮다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxYtiI%2Fbtr35FEVVmk%2FcS0dxRjlWfFukRPCaL9y61%2Fimg.png)\r\n\r\n위에 생성한 script는 통상적으로 댓글 부분에 해당되는 곳에 그대로 복사해 넣지만, 나는 Nextjs를 통해 댓글 컴포넌트를 생성할 것이기 때문에 다른 방식을 사용했다.\r\n\r\n```javascript\r\nexport const Comment = () => {\r\n return (\r\n {\r\n if (!elem) {\r\n return;\r\n }\r\n const scriptElem = document.createElement(\"script\");\r\n scriptElem.src = \"https://utteranc.es/client.js\";\r\n scriptElem.async = true;\r\n scriptElem.setAttribute(\"repo\", \"{utterances repo name}\");\r\n scriptElem.setAttribute(\"issue-term\", \"pathname\");\r\n scriptElem.setAttribute(\"theme\", \"github-dark\");\r\n scriptElem.setAttribute(\"label\", \"blog-comment\");\r\n scriptElem.crossOrigin = \"anonymous\";\r\n elem.appendChild(scriptElem);\r\n }}\r\n />\r\n );\r\n};\r\n```\r\n\r\n해당 컴포넌트에 사용하기 위해 기존의 script와 같은 속성들을 넣어줬다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fckai9y%2Fbtr4aKSLFRA%2F9p3HEGqDCEkfa8sg7Nnd0k%2Fimg.png)\r\n\r\n하지만 strict 모드를 false로 해도 종종 댓글찾이 2개가 되어버려 useEffect를 통해 1번만 script를 붙일 수 있도록 코드를 수정했다.\r\n\r\n```javascript\r\nimport { useEffect } from \"react\";\r\n\r\nexport const Comment = () => {\r\n const makeRef = (el: HTMLElement | null) => {\r\n if (!el) {\r\n return;\r\n } else {\r\n const scriptElem = document.createElement(\"script\");\r\n scriptElem.src = \"https://utteranc.es/client.js\";\r\n scriptElem.async = true;\r\n scriptElem.setAttribute(\"repo\", \"{utterances repo name}\");\r\n scriptElem.setAttribute(\"issue-term\", \"pathname\");\r\n scriptElem.setAttribute(\"theme\", \"github-dark\");\r\n scriptElem.setAttribute(\"label\", \"blog-comment\");\r\n scriptElem.crossOrigin = \"anonymous\";\r\n el.appendChild(scriptElem);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n const newEl = document.getElementById(\"comment-box\");\r\n makeRef(newEl);\r\n }, []);\r\n\r\n return
;\r\n};\r\n```\r\n\r\n코드 수정 후 원하는대로 동작하는 모습을 볼 수 있었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNZNaQ%2Fbtr38NhUPkk%2FSH2Dgj2Si71gH3Gykm9kYK%2Fimg.png)\r\n"},{"title":"github github api get repo data","description":"GithubAPI를 이용한 repository 내 Markdown 파일 정보 불러오기","category":"github","date":"2023-03-13","content":"\r\n# Github REST API 설명서\r\n\r\nhttps://docs.github.com/ko/rest?apiVersion=2022-11-28\r\n\r\n## GithubAPI 이용 시 토큰이 필요한 경우 아래와 같이 토큰 발급도 가능하다.\r\n\r\n- 우측 상단의 프로필을 통해 Settings 접근\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5d9NT%2Fbtr3XWt2ipS%2FlVrpZru5irR5mDft0cLaO0%2Fimg.png)\r\n\r\n- 좌측 하단의 Developer settings 접속\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnMYAN%2Fbtr37uQgb1j%2Fz3lG0JXTBq5gkIhvk1HEek%2Fimg.png)\r\n\r\n- Tokens나 Fine-grained tokens를 통해 원하는 권한들을 설정해 토큰을 생성할 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKOqYU%2Fbtr3V6YgATa%2Fc3QmI12TDR3vgoNL0ZTZR1%2Fimg.png)\r\n\r\n> Github 홈페이지에 여러 api들이 많기 때문에 필요에 따라 사용하면 좋을 것 같다. 이 글에서는 repository 안의 파일들의 정보만을 가져올 것이다.\r\n\r\n## 1. requestURL\r\n\r\n```javascript\r\nconst BASE_URL =\r\n \"https://api.github.com/repos/{유저명을 포함한/레포지토리 이름}/contents/{데이터를 가져올 디렉토리}\";\r\nconst options = {\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n },\r\n};\r\n```\r\n\r\n예를 들어 src의 posting이라는 디렉토리의 데이터들을 가져오고 싶다면\r\n\r\nhttps://api.github.com/repos/name/some-repo/contents/src/posting\r\n\r\n정도가 될 수 있다.\r\n\r\n요청이 정상적으로 작동했다면 아래와 같이 데이터를 받을 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXHDFb%2Fbtr3WcDXFiZ%2FmFW3Q3uyQPHEOBpkOFVKA0%2Fimg.png)\r\n\r\n하지만 하나의 파일에 대한 구체적인 정보는 들어있지 않다.\r\n\r\n한 단계로 더 들어가 정보를 받아온다면 아래와 같은 base64로 인코딩된 content 데이터를 받아올 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaVD0e%2Fbtr38M4cfZ6%2FMLUYZ4OarG3pXvjnSCW7VK%2Fimg.png)\r\n\r\n## 2. decoding\r\n\r\n해당 콘텐츠의 데이터를 정상적으로 받아오기 위해서는 디코딩이 필요하다.\r\n\r\n```typescript\r\nconst eachMarkdown = async (name: string) => {\r\n const url = `${BASE_URL}/${categoryName}/${name}`;\r\n const response = await axios.get(url, options);\r\n const content = Buffer.from(response.data.content, \"base64\").toString();\r\n\r\n ...\r\n};\r\n```\r\n\r\ncontent 데이터를 추출하고 콘솔을 확인하면 아래와 같이 string을 받아볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flctvp%2Fbtr38LKZ6o1%2FnwU9EK44kJmKCfanQsdw0K%2Fimg.png)\r\n\r\n## 3. 데이터 가공\r\n\r\n캡쳐 이미지 아래로 마크다운 파일의 본문들이 함께 있지만, 내가 필요한 부분은 머리말 부분이기 때문에 이 데이터를 추출해야 했다.\r\n\r\n```typescript\r\nconst eachMarkdown = async (name: string) => {\r\n const url = `${BASE_URL}/${categoryName}/${name}`;\r\n const response = await axios.get(url, options);\r\n const content = Buffer.from(response.data.content, \"base64\").toString();\r\n const frontmatter = content.match(/^---\\n([\\s\\S]+?)\\n---/);\r\n if (frontmatter) {\r\n const matters = JSON.stringify(frontmatter[0]).split(\"\\\\n\");\r\n const dateRegex = /\\d{4}-\\d{2}-\\d{2}/;\r\n const match = matters[4].match(dateRegex);\r\n if (match) {\r\n const result = {\r\n title: matters[1].split(\"title: \")[1].replaceAll(\" \", \"-\"),\r\n description: matters[2].split(\"description: \")[1],\r\n category: matters[3].split(\"category: \")[1],\r\n date: match[0],\r\n };\r\n }\r\n }\r\n};\r\n```\r\n\r\n상수 frontmatter는 머리말을 가져오지만 예쁘게 가져오진 않는다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTC1yi%2Fbtr35bRukP2%2FnWJ0mgeFgu2LlEBk0KyupK%2Fimg.png)\r\n\r\n때문에 if(frontmatter){...}와 같이 필요한 데이터를 정규식이나 string메서드를 통해 원하는대로 가공해 사용하여 사용해야 한다.\r\n"},{"title":"github git use markdown viewer","description":"Github pages 블로그 Markdown 불러오기","category":"github","date":"2023-03-12","content":"\r\n## Markdown을 통해 블로그를 작성하는 이유\r\n\r\n- 머리말을 따로 설정해 route 링크를 설정해주거나 제목, 날짜, 카테고리, 등을 설정하여 블로그 탐색을 용이하게 해줄 수 있다.\r\n- Markdown을 repository에 저장함으로써 githubAPI 등을 이용한 데이터 조회를 가능하게 한다.\r\n\r\n## 필요한 패키지\r\n\r\n```bash\r\nyarn add gray-matter # 문자열이나 파일에서 머리말을 구문 분석, 파일에서 메타데이터와 내용 등 추출 시 사용\r\nyarn add marked\r\nyarn add @types/marked # 타입스크립트의 경우\r\nyarn add react-markdown #
과 유사함\r\nyarn add remark-gfm # 마크다운 문법이 다양하게 적용될 수 있도록 도와주는 플러그인\r\nyarn add react-syntax-highlighter\r\nyarn add @types/react-syntax-highlighter # 타입스크립트의 경우\r\n```\r\n\r\n참조\r\n\r\nhttps://yarnpkg.com/package/react-markdown\r\n\r\nhttps://yarnpkg.com/package/react-syntax-highlighter\r\n\r\n## posting 파일들은 src 파일 안에 있다고 가정\r\n\r\n## 1. [slug].tsx 파일 생성\r\n\r\n```javascript\r\n\r\nimport { GetStaticPaths, GetStaticProps } from \"next\";\r\nimport { join } from \"path\";\r\nimport fs from \"fs/promises\";\r\nimport matter from \"gray-matter\";\r\nimport ReactMarkdown from \"react-markdown\";\r\nimport remarkGfm from \"remark-gfm\"; // Link, table, checklist 등의 형식을 표현할 수 있게 해줌\r\nimport { Prism as SyntaxHighlighter } from \"react-syntax-highlighter\";\r\n// import { darcula } from \"react-syntax-highlighter/dist/esm/styles/prism\"; - 에러 발생\r\nimport { dark } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\nimport { vsDark } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\nimport { darcula } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\n\r\n\r\ninterface Props {\r\n title: string;\r\n date: string;\r\n content: string;\r\n}\r\n\r\nexport default function BlogPost({ title, date, content }: Props) {\r\n return (\r\n
\r\n

{title}

\r\n

{date}

\r\n \r\n {String(children).replace(/\\n$/, \"\")}\r\n \r\n ) : (\r\n \r\n {children}\r\n \r\n );\r\n },\r\n }}\r\n >\r\n {content}\r\n \r\n
\r\n );\r\n}\r\n\r\n// 파일명을 통해 url 생성\r\nexport const getStaticPaths: GetStaticPaths = async () => {\r\n const postsDirectory = join(process.cwd() + \"src\" + \"/posting\", \"blog\");\r\n const filenames = await fs.readdir(postsDirectory);\r\n const paths = filenames.map(filename => ({\r\n params: { slug: filename.replace(/\\.md$/, \"\") },\r\n }));\r\n return { paths, fallback: false };\r\n};\r\n\r\n// url에 해당하는 파일명을 찾아 matter로 데이터 추출\r\nexport const getStaticProps: GetStaticProps = async ({ params }) => {\r\n const slug = params?.slug as string;\r\n const filePath = join(process.cwd() + \"src\" + \"/posting\", \"blog\", `${slug}.md`);\r\n const fileContents = await fs.readFile(filePath, \"utf8\");\r\n const { data, content } = matter(fileContents);\r\n return {\r\n props: {\r\n title: data.title,\r\n date: data.date,\r\n content,\r\n },\r\n };\r\n};\r\n\r\n```\r\n\r\n## 2. SyntaxHighlighter style 설정\r\n\r\nSyntaxHighlighter 내부의 코드블록 style을 이용하기 위해서는 그에 맞는 소스를 가져와야 한다.\r\n\r\n### 에러가 발생하는 ...dist/esm/styles/prism\r\n\r\nimport { darcula } from \"react-syntax-highlighter/dist/esm/styles/prism\";\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrNEOn%2Fbtr34VAZ3qk%2Fhy9KPHftPMrHNswKxBdBk0%2Fimg.png)\r\n\r\n### 정상 작동하는 imports ...dist/cjs/styles/prism\r\n\r\n```javascript\r\nimport { darcula } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQTv0s%2Fbtr37t4PDmS%2Fc0Sw6mnao0GUU05pG0mnb0%2Fimg.png)\r\n\r\n```javascript\r\nimport { dark } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdjz47g%2Fbtr35dhlOF6%2FmmOkOQB6R1x5TgzkEcQ27K%2Fimg.png)\r\n\r\n```javascript\r\nimport { vsDark } from \"react-syntax-highlighter/dist/cjs/styles/prism\";\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F29uf2%2Fbtr3V6KDHYg%2FeTnSoSaIAgZYqOcK7MTr8K%2Fimg.png)\r\n\r\n원하는 스타일을 SyntaxHighlighter style에 넣어주면 된다.\r\n\r\n> Markdown content(내용)는 ReactMarkdown 컴포넌트의 className을 통해 수정해줄 수 있다.\r\n\r\n## 3. Markdown 파일 생성\r\n\r\n```markdown\r\n// 파일명 : first-post.md\r\n--- 머리말\r\ntitle: First-Post\r\ndate: \"2023-02-28\" <- 숫자만 사용하면 newDate()처럼 생성되어 타입 오류 발생 가능\r\n\r\n---\r\n\r\n# Deploy and test\r\n\r\ntest\r\n```\r\n\r\nMarkdown 파일의 데이터를 불러올때, 머리말을 이용한 metaTag 설정이나 제목 등을 자유롭게 설정할 수 있다.\r\n\r\n파일 생성 후 해당 링크 접속 시 정상적으로 접속이 가능해진다.\r\n\r\n## ex\r\n\r\nlocalhost:3000/first-post\r\n\r\nurl은 [slug].tsx 파일이 위치한 경로에 따라 달라질 수 있다. (nextjs 라우팅 방식)\r\n"},{"title":"github git custom blog","description":"Nextjs를 이용한 Github pages 블로그 만들기","category":"github","date":"2023-03-11","content":"\r\n# Github pages와 Nextjs를 선택한 이유\r\n\r\n### 1. Github pages\r\n\r\n> vercel도 Nextjs로 쉽게 배포할 수 있지만, 백엔드는 별도로 만들지 않을 계획이었기에, 추후 댓글 기능이나 추천 글 목록 등을 Github 사이트 하나로 모두 관리하기 위해서 Github pages를 이용하기로 했다.\r\n\r\n### 2. Nextjs\r\n\r\n> 블로그는 검색엔진에 잘 노출되어야 그 의미가 있다. 또한 React 환경을 이용한 블로그 개발을 원했기에 SSR(Server Side Rendering) 등을 통한 SEO(검색엔진 최적화)에 유리한 React 프레임워크인 Nextjs를 사용하기로 결정했다.\r\n\r\n# Github Pages 호스팅 방법\r\n\r\n### 1. Repository 생성\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkBnCf%2Fbtr3TGZh4t7%2F0VD4ycS4u8EvTAeIxvjsP1%2Fimg.png)\r\n\r\nRepo 이름은 {username}.github.io 로 저장해준다.\r\n\r\n- 생성된 repo의 settings => pages에서 배포를 확인할 수 있다. (배포되는데 5~10분 정도 소요될 수 있다.)\r\n\r\n### 2. Repository 클론 후 Nextjs 설치\r\n\r\n```bash\r\n# 현재 디렉토리에 설치\r\nyarn create next-app .\r\n\r\n# typescript\r\nyarn create next-app . --typescript\r\n```\r\n\r\n### 3. gh-pages 설치\r\n\r\nGithub pages를 이용하기 위해 아래 명령어를 통해 설치해준다.\r\n\r\n```bash\r\nyarn add gh-pages --save-dev\r\n```\r\n\r\n### 4. package.json 설정\r\n\r\n홈페이지 도메인 추가 및 scripts를 수정해준다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSbOFc%2Fbtr33yMA8BY%2FYBMjkb8aNMMR8jv7bhlrxk%2Fimg.png)\r\n\r\n1번째 줄처럼 홈페이지를 입력하고, scripts를 위와 같이 수정해준다.\r\n\r\n```javascript\r\n\"build\": \"next build && next export\",\r\n\"predeploy\": \"npm run build\",\r\n\"deploy\": \"touch out/.nojekyll && gh-pages -d out --dotfiles\"\r\n```\r\n\r\n- next export : Nextjs를 정적 사이트로 배포하기 위해 설정\r\n- -- touch out/.nojekyll : 본래 guthub pages는 jekyll 기반으로, \\_\\_{filename}을 특수 리소스로 간주하고 최종 사이트에 복사하지 않기 때문에 jekyll을 사용하지 않는다고 명시해 정상적으로 처리될 수 있도록 설정\r\n- gh-pages -d out --dotfiles : 현재 repo의 임시 복제본 생성 후, gh-pages 브랜치가 없는 경우 브랜치 생성. 기본 경로의 모든 파일 또는 선택적 src 구성의 패턴과 일치하는 파일만 복사하고 모든 변경 사항 커밋 후 푸시. 이미 gh-pages 브랜치가 있는 경우 제공된 파일에서 커밋을 추가하기 전에 원격의 모든 커밋으로 업데이트됨. src 패턴과 일치하는 도트 파일도 포함시킴\r\n\r\n참조 : https://github.com/tschaub/gh-pages\r\n\r\n명령어 보기\r\n\r\n```bash\r\nyarn gh-pages --help\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJQQKR%2Fbtr34cpsLGR%2FDtpXfGfLf2p8bph05dvvnk%2Fimg.png)\r\n\r\n### next.config.js 수정\r\n\r\nconfig는 본인이 적용하려는 방식에 맞게 설정해줘도 괜찮다.\r\n\r\n```javascript\r\n/** @type {import('next').NextConfig} */\r\nconst nextConfig = {\r\n reactStrictMode: false,\r\n images: {\r\n unoptimized: true, // 이미지 정상적으로 불러올 수 있도록함\r\n },\r\n compiler: {\r\n styledComponents: true, // styled-components 사용 시 컴파일러에 추가\r\n },\r\n};\r\n\r\nmodule.exports = nextConfig;\r\n```\r\n\r\n### 6. 배포\r\n\r\n```bash\r\nyarn deploy\r\n```\r\n\r\n> cmd(명령 프롬프트)에서 실행 시 tought를 사용할 수 없다는 에러가 발생하기 때문에 git bash로 실행\r\n\r\n### 7. 배포 후 설정\r\n\r\n```bash\r\nExport successful. Files written to D:\\0. CORDING\\INDI-PROJECT\\github-blog\\out\r\n$ gh-pages -d out --dotfiles\r\nPublished\r\n```\r\n\r\n터미널에 위와 같은 메세지가 뜨면, repository => setting => pages의 branch에 gh-pages가 생성돼있는 것을 볼 수 있음\r\n\r\ngh-pages를 배포 브랜치로 설정해준 후 저장\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXcGvE%2Fbtr35F5rS1r%2FgTSz4ck3EW2y6KOnmS9EiK%2Fimg.png)\r\n\r\n설정을 완료하고 상단의 Visit site로 페이지를 접속하면 Nextjs 기본 화면이 잘 나오는 것을 볼 수 있다.\r\n"},{"title":"css a tag style fix","description":"a 태그(Link 태그) 기본 스타일 제거하기","category":"css","date":"2023-03-09","content":"\r\n# a태그 기본 스타일 제거와 스타일 적용\r\n\r\n```css\r\n/* 기본 밑줄 제거 */\r\na {\r\n text-decoration: none;\r\n}\r\n\r\n/* 아직 방문하지 않은 링크의 글자색 정의 */\r\na:link {\r\n color: pink;\r\n}\r\n\r\n/* 사용자가 방문한 적이 있는(클릭한 이후) 링크의 글자색 \r\n정의 */\r\na:visited {\r\n color: black;\r\n}\r\n\r\n/* 마우스를 링크에 올려두었을 때, 글자색을 정의 */\r\na:hover {\r\n color: red;\r\n}\r\n\r\n/* 마우스로 링크를 클릭하고 뗄 때까지의 글자색을 정의 */\r\na:active {\r\n color: green;\r\n}\r\n```\r\n"},{"title":"css no selection","description":"CSS로 글자 더블클릭 시 선택자(파란색 마스킹) 없애기","category":"css","date":"2023-03-09","content":"\r\n# 더블클릭시 파란색 선택자 생성 막는 방법\r\n\r\n```css\r\ndiv {\r\n user-select: none;\r\n -webkit-user-select: none;\r\n /* (safari, chrome) browsers */\r\n -moz-user-select: none;\r\n /* mozilla browsers */\r\n -khtml-user-select: none;\r\n /* konqueror browsers */\r\n\r\n -ms-user-select: none; /* IE10+ */\r\n}\r\n```\r\n"},{"title":"github git cmd","description":"깃 명령어 모음","category":"github","date":"2023-03-09","content":"\r\n# 깃 명령어 모음\r\n\r\n## 디렉토리\r\n\r\n1. 홈 디렉토리로 이동\r\n\r\n```bash\r\n$ cd ~\r\n```\r\n\r\n2. 새 디렉토리에 디렉토리명을 생성\r\n\r\n```bash\r\n$ mkdir [디렉토리명]\r\n```\r\n\r\n3. [디렉토리명]으로 이동\r\n\r\n```bash\r\n$ cd [디렉토리명]\r\n```\r\n\r\n4. 부모 디렉토리로 이동\r\n\r\n```bash\r\n$ cd ..\r\n```\r\n\r\n5. 현재 경로를 출력 // print working directory\r\n\r\n```bash\r\n$ pwd\r\n```\r\n\r\n6. 디렉토리의 내용을 출력\r\n\r\n```bash\r\n$ ls\r\n```\r\n\r\n7. 디렉토리의 폴더 상세 정보까지 출력\r\n\r\n```bash\r\n$ ls -l\r\n```\r\n\r\n8. 디렉토리의 숨김 파일과 디렉토리까지 출력\r\n\r\n```bash\r\n$ ls -a\r\n```\r\n\r\ntip: 옵션은 합쳐서 ls -al 처럼 사용이 가능하다.\r\n\r\n9. 파일이 있는 디렉토리를 내용물과 함께 삭제\r\n\r\n```bash\r\n$ rm -r [디렉토리명]\r\n```\r\n\r\n10. vi 편집기로 [파일명.확장자명] 파일을 작성\r\n\r\n```bash\r\n$ vim [파일명.확장자명]\r\n```\r\n\r\n11. 터미널 창의 내용을 삭제\r\n\r\n```bash\r\n$ clear\r\n```\r\n\r\n12. 터미널 창을 종료\r\n\r\n```bash\r\n$ exit\r\n```\r\n\r\n## git 유저 / 업로드\r\n\r\n1. 현재 위치에서 지역 저장소를 생성\r\n\r\n```bash\r\n$ git init\r\n```\r\n\r\n2. 깃 환경에서 사용자 이름을 [사용자명]으로 지정\r\n\r\n```bash\r\n$ git config --global user.name \"[사용자명]\"\r\n```\r\n\r\n3. 깃 환경에서 사용자 이메일을 [사용자이메일명]으로 지정\r\n\r\n```bash\r\n$ git config --global user.email \"[사용자이메일명]\"\r\n```\r\n\r\n4. 깃의 상태를 확인\r\n\r\n```bash\r\n$ git status\r\n```\r\n\r\n## commit 명령어\r\n\r\n1. [파일명.확장자명]을 스테이지에 올림\r\n\r\n```bash\r\n$ git add [파일명.확장자명]\r\n```\r\n\r\n2. 커밋 메시지 [메시지명]을 붙여 커밋\r\n\r\n```bash\r\n$ git commit -m \"[메시지명]\"\r\n```\r\n\r\n3. 메시지 [메시지명]을 붙여서 스테이징과 커밋을 동시에 진행\r\n\r\n```bash\r\n$ git commit -a -m \"[메시지명]\"\r\n```\r\n\r\n4. 커밋 내역 확인\r\n\r\n```bash\r\n$ git log\r\n$ git log --pretty=oneline # 한줄로 표기하기\r\n```\r\n\r\n5. 특정 커밋 내역 확인\r\n\r\n```bash\r\n$ git show [커밋 id]\r\n```\r\n\r\n6. 최근 버전과 작업 폴더의 수정 파일 사이의 차이를 출력\r\n\r\n```bash\r\n$ git diff\r\n$ git diff [이전커밋 id] [이후커밋 id]\r\n```\r\n\r\n7. 지정한 커밋 해시로 이동\r\n\r\n```bash\r\n$ git checkout [커밋 해시]\r\n```\r\n\r\n8. 가장 최근 커밋을 취소\r\n\r\n```bash\r\n$ git reset HEAD^ # 현재 HEAD의 이전 커밋으로 되돌리기\r\n$ git reset HEAD~n # 현재로 부터 n 번째 이전 커밋으로 되돌리기\r\n```\r\n\r\n9. 지정한 커밋 해시로 이동하고 커밋을 취소\r\n\r\n```bash\r\n$ git reset [커밋 해시]\r\n\r\n# reset의 3가지 옵션\r\n$ git reset --soft [커밋ID] # head 만 바뀜\r\n$ git reset --mixed [커밋ID] # staging 도 그 때로 바뀜\r\n$ git reset --hard [커밋ID] # working디렉토리/staging 모두 그 때로 바꿈\r\n```\r\n\r\n10. 지정한 커밋 해시의 변경 이력을 취소\r\n\r\n```bash\r\n$ git revert [커밋 해시]\r\n```\r\n\r\n11. 커밋 수정하는 법\r\n\r\n```bash\r\n# 파일 수정 한 뒤\r\n$ git add .\r\n$ git commit --amend : 최신 커밋 수정\r\n```\r\n\r\n## 브랜치 명령어\r\n\r\n1. 새로운 브랜치 [브랜치명]을 생성\r\n\r\n```bash\r\n$ git branch [브랜치명]\r\n# 브랜치 조회하기\r\n$ git branch\r\n```\r\n\r\n2. [브랜치명]으로 체크아웃(이동)\r\n\r\n```bash\r\n$ git checkout [브랜치명]\r\n$ git checkout -b [브랜치명] # 브랜치만들고 바로 이동\r\n# 브랜치 삭제\r\n$ git branch -d 브랜치명\r\n```\r\n\r\n3. 커밋 로그에서 한 줄에 한 커밋씩 출력\r\n\r\n```bash\r\n$ git log --oneline\r\n```\r\n\r\n4. 수정한 전체 파일을 스페이지에 올린다.\r\n\r\n```bash\r\n$ git add .\r\n```\r\n\r\n5. 커밋 로그에 각 브랜치의 커밋을 그래프로 표시\r\n\r\n```bash\r\n$ git log --branches --graph\r\n```\r\n\r\n6. [브랜치명]을 master 브랜치와 병합\r\n\r\n```bash\r\n$ git merge [브랜치명]\r\n$ git merge [브랜치명] --edit # 병합 후 바로 vi 편집기가 나오면서 커밋 메시지 수정 가능\r\n$ git merge [브랜치명] --no-edit # 커밋 메시지 수정없이 바로 병합\r\n```\r\n\r\n7. merge 취소하기\r\n\r\n```bash\r\n$ git merge --abort\r\n```\r\n\r\n## git hub 원격 저장소\r\n\r\n1. 원격 저장소에 연결\r\n\r\n```bash\r\n$ git remote add origin [github 레포지 주소]\r\n$ git remote add origin [branch 이름] #없으면 생성됨\r\n```\r\n\r\n2. 원격 저장소에 연결됐는지 확인\r\n\r\n```bash\r\n$ git remote -v\r\n```\r\n\r\n3. 지역 저장소의 커밋을 맨 처음 원격 저장소에 올리는 경우\r\n\r\n```bash\r\n$ git push -u origin master\r\n```\r\n\r\n4. 3번을 한 후에 지역 저장소의 커밋을 원격 저장소에 올리는 경우(업로드)\r\n\r\n```bash\r\n$ git push\r\n$ git push origin master\r\n```\r\n\r\n5. 원격 저장소의 커밋을 지역 저장소로 가져옴\r\n\r\n```bash\r\n$ git pull\r\n$ git pull origin master\r\n```\r\n\r\n6. SSH 키를 생성함\r\n\r\n```bash\r\n$ ssh-keygen\r\n```\r\n\r\n7. 원격 저장소 복제하기\r\n\r\n```bash\r\n# 첫번째 커밋이 아니라면 풀 먼저하기\r\n$ git remote remove origin\r\n\r\n#원격 저장소를 [지역저장소명]에 복제하기\r\n$ git clone [원격 저장소 주소]\r\n```\r\n\r\n8. 원격 저장소의 커밋을 가져오기만 하고 merge하지 않는다\r\n 가져온 branch 내용은 origin/[브랜치] 로 저장됨\r\n\r\n```bash\r\n$ git fetch\r\n# 이후엔 diff 로 비교\r\n$ git diff test origin/test # 브랜치 이름이 test일 경우 예시\r\n```\r\n\r\n9. 패치로 가져온 정보가 있는 브랜치\\[FETCH_HEAD\\]로 이동\r\n\r\n```bash\r\n$ git checkout FETCH_HEAD\r\n```\r\n\r\n10. 패치로 가져온 정보가 있는 브랜치[FETCH_HEAD]를 master 브랜치에 병합한다\r\n\r\n```bash\r\n$ git merge FETCH_HEAD\r\n```\r\n\r\n11. [브랜치명]을 만드는 것과 동시에 체크아웃한다\r\n\r\n```bash\r\n$ git checkout -b [브랜치명]\r\n```\r\n\r\n12. 원격 저장소에 [브랜치명]의 브랜치의 커밋을 올린다\r\n\r\n```bash\r\n$ git push origin [브랜치명]\r\n```\r\n\r\n13. 원격저장소 삭제\r\n\r\n```bash\r\n$ git remote remove origin\r\n```\r\n\r\n## 파일/보관 명령어(stash)\r\n\r\n1. 파일 내용 출력\r\n\r\n```bash\r\n$ cat [파일명.확장자명]\r\n```\r\n\r\n2. 디렉토리를 만드는 동시에 지역저장소 생성\r\n\r\n```bash\r\n$ cd init [디렉토리명]\r\n```\r\n\r\n3. 현재 커밋을 다른 브랜치에 있는 [커밋메시지] 커밋으로 되돌림\r\n\r\n```bash\r\n$ git reset [커밋메시지] [커밋해시]\r\n```\r\n\r\n4. 병합이 끝난 [브랜치명]을 삭제\r\n\r\n```bash\r\n$ git branch [브랜치명] -d\r\n```\r\n\r\n5. 작업 트리의 수정 내용 stash에 따로 보관하기\r\n\r\n```bash\r\n$ git stash\r\n$ git stash save\r\n```\r\n\r\n6. 보관한 내용을 목록을 출력\r\n\r\n```bash\r\n$ git stash list\r\n```\r\n\r\n7. 보관한 내용을 적용\r\n\r\n```bash\r\n$ git stash apply\r\n$ git stash apply stash@{1}\r\n```\r\n\r\n8. 보관한 내용 중 가장 최근 항목을 삭제\r\n\r\n```bash\r\n$ git stash drop\r\n$ git stash drop stash@{1}\r\n```\r\n\r\n9. stash를 apply하고 제거(drop) 하기\r\n\r\n```bash\r\n$ git stash pop\r\n```\r\n\r\n## 기타 명령어\r\n\r\n1. 긴 명령어 짧게 이름 붙여 사용하기\r\n\r\n```bash\r\n# ex) git log --pretty=oneline을\r\n# ->git history 라는 별명으로 바꾸기.\r\n\r\ngit config alias.[별명] '원하는 명령어'\r\ngit config alias.history 'log --pretty=oneline'\r\n```\r\n\r\n2. tag 설정 하기\r\n\r\n```bash\r\n$ git tag [태그이름][커밋 ID]\r\n$ git tag Version_2 86a99 # tag 달기\r\n$ git tag #tag 조회하기\r\n$ git show Version_2\r\n```\r\n"},{"title":"html meta tag","description":"SEO(검색엔진 최적화)를 위한 HTML의 meta tag 간단 정리","category":"html","date":"2023-03-09","content":"\r\n# 메타태그\r\n\r\n### 메타태그란 쉽게 말해서 눈에 보이지 않는 문서가 가지고 있는 정보들을 담을 수 있는 태그를 말한다.\r\n\r\n## 1. 주로 사용해볼 수 있는 요소\r\n\r\n검색 엔진에 검색되는 단어 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n홈페이지 주제 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n제목 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n검색 결과에 표시되는 문자 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n검색 로봇 제어\r\n\r\n```html\r\n\r\n```\r\n\r\n날짜(제작일)\r\n\r\n```html\r\n\r\n```\r\n\r\n제작자명 지정\r\n\r\n```html\r\n\r\n```\r\n\r\nog: 태그\r\n\r\n```html\r\n\r\n\r\n\r\n\r\n```\r\n\r\n## 2. 사용 빈도 낮아보이는 요소\r\n\r\n제작사 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n웹 책임자 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n제작 도구 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n메일 주소 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n```html\r\n\r\n```\r\n\r\n파일 이름 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n위치 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n배포자 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n저작권 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n제작 년/월/일 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n최종 수정일 정의\r\n\r\n```html\r\n\r\n```\r\n\r\n## 3. 사용 빈도가 많이 낮아보이는 요소\r\n\r\n그림 위에 마우스 오버시 이미지 관련 툴바 생성하지 않음\r\n\r\n```html\r\n\r\n```\r\n\r\n캐시 금지\r\n\r\n```html\r\n\r\n```\r\n\r\n```html\r\n\r\n```\r\n\r\n캐시 만료일 정의\r\n\r\n```html\r\n\r\n```\r\n\r\n60초마다 새로고침\r\n\r\n```html\r\n\r\n```\r\n\r\n입력한 주소로 5초후 이동하는 것을 정의\r\n\r\n```html\r\n\r\n```\r\n\r\n브라우저 호환성 지정\r\n\r\n```html\r\n\r\n```\r\n\r\n웹 페이지에 쓰인 언어 지정\r\n\r\n````html\r\n\r\n```html\r\n\r\n````\r\n\r\n## 4. 써볼만한 태그\r\n\r\n페이지 인입 시 장면 전환 효과\r\n\r\n```html\r\n\r\n```\r\n\r\n- Box out : 네모난 박스가 안쪽에서 바깥쪽으로\r\n- Circle in : 원이 바깥에서 안쪽으로\r\n- Circle out : 원이 안쪽에서 바깥쪽으로\r\n- Wipe up : 이미지의 아래에서 위쪽으로 수직 이동\r\n- Wipe down : 이미지의 위에서 아래쪽으로 수직 이동\r\n- Wipe right : 이미지의 왼쪽에서 오른쪽으로 수평 이동\r\n- Wipe left : 이미지의 오른쪽에서 왼쪽으로 수평 이동\r\n- Vertical blinds : 수직 블라인드가 쳐지는 형태로 변환\r\n- Horizontal blinds : 수평 블라인드가 쳐지는 형태로 변환\r\n- Checkerboard across : 바둑판 형태의 격자가 왼쪽에서 오른쪽으로 - 생성\r\n- Checkerboard down : 바둑판 형태의 격자가 위에서 아래로 생성\r\n- Random dissove : 안개와 비슷한 형태로 전환\r\n- Split vertical in : 왼쪽과 오른쪽 끝에서 중앙으로 수직 이동\r\n- Split vertical out : 중앙에서 양쪽 끝으로 수직 이동\r\n- Split Horizontal in : 양쪽에서 중앙으로 수평 이동\r\n- Split Horizontal out : 중앙에서 양쪽끝으로 수직이동\r\n- Strips left down : 대각선 형태로 오른쪽 상단에서 왼쪽 하단으로 - 이동\r\n- Strips left up : 대각선 형태로 오른쪽 하단에서 왼쪽 상단으로 이동\r\n- Strips right down : 대각선 형태로 왼쪽 상단에서 오른쪽 하단으로 - 이동\r\n- Strips right up : 대각선 형태로 왼쪽 하단에서 오른쪽 상단으로 이동\r\n- Random bars horizontal : 수평선이 무작위로 생성\r\n- Random bars vertical : 수직선이 무작위로 생성\r\n- Random : 임의로 생성\r\n"},{"title":"javascript basic 1 num str","description":"javascript 기초 1 숫자열문자열","category":"javascript","date":"2023-03-06","content":"\r\n# 1. 숫자열을 문자열로 바꾸기\r\n\r\n```javascript\r\n\r\n// 숫자는 숫자+\"\"만 해줘도 (혹은 다른 문자를 더해줘도) 문자열로 바뀐다.\r\n\r\nlet a = 1234\r\nconsole.log(a) // a는 숫자 1234\r\nlet b = a+\"\"\r\nconsole.log(b) // b는 문자 '1234'\r\nlet c = 1234.toString()\r\nconsole.log(c) // c는 문자 '1234\r\n\r\n```\r\n\r\n# 2. 문자열을 숫자열로 바꾸기\r\n\r\n```javascript\r\n\r\n//s는 String\r\nNumber(s)로 문자열을 숫자로 변환 - 인자를 바꿀 수 없으면(ex. 123입니다) NaN을 리턴한다.\r\nparseInt(s)로 문자열을 숫자로 변환 - 정수나 NaN을 리턴한다.\r\nparseFloat(s)로 문자열을 숫자로 변환 - 부동 소수점 숫자를 반환한다.\r\n\r\n// parseFloat 예제\r\n\r\nfunction circumference(r) {\r\n return parseFloat(r) * 2.0 * Math.PI;\r\n}\r\n\r\nconsole.log(circumference(4.567));\r\n// 28.695307297889173\r\n\r\nconsole.log(circumference('4.567abcdefgh'));\r\n// 28.695307297889173\r\n\r\nconsole.log(circumference('abcdefgh'));\r\n// NaN\r\n\r\n\r\n```\r\n\r\n# 3. 문자열 다루기\r\n\r\n```javascript\r\n\r\nString.trim() - 문자열의 공백을 제거\r\n .trimStart() - 문자열 왼쪽 공백 제거\r\n .trimEnd() - 오른쪽 공백 제거\r\n .slice(0,-1) - 문자열에서 텍스트를 추출하고 새 문자열을 반환\r\n .split('') - 문자열 String을 ()안 인자(''등)로 나눠 각 글자별로 배열 생성\r\n \t\t// ex) let name=hanbbi; name.split('') // [ 'h', 'a', 'n', 'b', 'b', 'i' ]\r\n .join('') 배열 내 요소들을 인자를 통해 이어준다.\r\n .replace(\"인자1\",\"인자2\") - 문자 내 맨 처음 발견된 인자1을 찾아 인자2로 바꿔준다.\r\n .replace(\"인자1\",\"인자2\") - 문자 내 모든 인자1을 찾아 인자2로 바꿔준다.\r\n .repeat() - 해당 문자를 ()번 반복\r\n .includes() - 문자열이 특정 문자열을 포함하는지 확인\r\n .charAt(0) - 스트링의 0번째 글자 가져오기(result = string)\r\n .substring(0,5) - 스트링의 0~4번째 글자 가져오기\r\n\r\n```\r\n"},{"title":"javascript basic 2 date","description":"javascript 기초 2 날짜다루기","category":"javascript","date":"2023-03-06","content":"\r\n# Javascript 날짜 다루기\r\n\r\n## 날짜 계산\r\n\r\n```javascript\r\n// 날짜 예제\r\n// Sunday - Saturday : 0 - 6\r\nconst someday = new Date(\"2022-01-23\");\r\n// 날짜 형식은 자유로움 ('january 23 ,2022') 처럼 사용 가능\r\n\r\nconst day1 = someday.getDay();\r\nconsole.log(day1); // 22.01.23은 일요일이므로, 0 반환\r\n\r\n// 날짜 추출\r\nlet today = new Date(); // 인자가 비어있는 경우 현재 날짜/시간 반환\r\n\r\nlet year = today?.getFullYear();\r\nconsole.log(year); // 123 - 2023년 반환값 (2022년 반환값 = 122)\r\n\r\nlet month = today?.getMonth() + 1; // javascript에서는 1월을 0부터 계산해주기 떄문에 + 1을 해준다.\r\nconsole.log(month); // 3\r\n\r\nlet day = today?.getDate();\r\nconsole.log(day); // 6\r\n\r\nlet hour = today?.getHour();\r\nconsole.log(hour); // 현재 시간 추출\r\n\r\nlet minutes = today?.getMinutes();\r\nconsole.log(minutes); // 현재 분 추출\r\n\r\nlet seconds = today?.getSeconds();\r\nconsole.log(seconds); // 현재 초 추출\r\n```\r\n"},{"title":"javascript basic 3 math","description":"javascript 기초 3 Math 다뤄보기","category":"javascript","date":"2023-03-06","content":"\r\n# Javascript Math 메서드\r\n\r\n## 1. 소수점 표현\r\n\r\n```javascript\r\n// n = Number\r\nMath.ceil(n); // 올림\r\nMath.floor(n); // 내림\r\nMath.round(n); // 반올림\r\nMath.toFixed(n); // 소수점 n의 자릿수까지 표현 / 매개변수가 없으면 1의자릿수 반환\r\n\r\n// .toFixed 예제\r\nlet num = 10.987654321;\r\n\r\nnum.toFixed(); // 11\r\nnum.toFixed(1); // 11.0\r\nnum.toFixed(2); // 10.99\r\nnum.toFixed(3); // 10.988\r\nnum.toFixed(4); // 10.9877\r\nnum.toFixed(5); // 10.98765\r\nnum.toFixed(10); // 10.9876543210\r\nnum.toFixed(14); // 10.98765432100000\r\n```\r\n\r\n## 2. 계산/정수 처리\r\n\r\n```javascript\r\nMath.abs(); // 절대값 반환\r\nMath.max(); // 인자 중 가장 큰 값 반환 ex. Math.max(1,2,3) - 3 , Math.max([4,5,6]) - 6\r\nMath.min(); // 인자 중 가장 작은 값 반환 - Math.max 반대\r\nMath.pow(x, y); // 인자간 거듭제곱 값 반환 === x**y와 동일\r\nMath.random(); // 0이상 1미만 랜덤값 반환\r\nMath.sqrt(); // 제곱근 반환\r\n```\r\n"},{"title":"javascript basic 4 array 1","description":"javascript 기초 4 배열 다루기 - 1","category":"javascript","date":"2023-03-06","content":"\r\n# JavaScript 배열 다뤄보기\r\n\r\n## 1. 배열 수정\r\n\r\n```javascript\r\nlet Arr = [1, 2, 3, 4, 5];\r\n\r\n// Arr배열의 0번째를 123으로 수정\r\nArr[0] = 123; // Arr = [123,2,3,4,5]\r\n```\r\n\r\n## 2. 배열 정렬 (Array.sort())\r\n\r\narray.sort()는 매개변수가 입력되지 않으면 유니코드 순서에 따라 정렬된다.\r\n\r\n또한 sort()는 원본 array를 참조하기 때문에 sort()를 통한 정렬은 원본 array도 동일하게 변경된다.\r\n\r\n### 숫자 배열 정렬\r\n\r\n```javascript\r\nlet arr = [1, 3, 2, 4];\r\n\r\narr.sort(); // [1,2,3,4]\r\narr.sort((a, b) => a - b); // [1,2,3,4]\r\narr.sort((a, b) => b - a); // [4,3,2,1]\r\n```\r\n\r\n### 문자 배열 정렬 (대소문자 구분x)\r\n\r\n```javascript\r\nlet arr3 = [\"jon\", \"awre\", \"gwgw\", \"zgfzg\"];\r\n\r\n// 오름차순\r\narr.sort(function (a, b) {\r\n const upperCaseA = a.toUpperCase();\r\n const upperCaseB = b.toUpperCase();\r\n\r\n if (upperCaseA > upperCaseB) return 1;\r\n if (upperCaseA < upperCaseB) return -1;\r\n if (upperCaseA === upperCaseB) return 0;\r\n}); // ['awre', 'gwgw', 'jon', 'zgfzg']\r\n\r\n// 내림차순\r\narr.sort(function (a, b) {\r\n const upperCaseA = a.toUpperCase();\r\n const upperCaseB = b.toUpperCase();\r\n\r\n if (upperCaseA < upperCaseB) return 1;\r\n if (upperCaseA > upperCaseB) return -1;\r\n if (upperCaseA === upperCaseB) return 0;\r\n}); // ['zgfzg', 'jon', 'gwgw', 'awre']\r\n```\r\n\r\n### 영어가 아닌 한글 등의 문자 정렬\r\n\r\n```javascript\r\nlet arr = [\"가\", \"나\", \"바\", \"다\"];\r\n\r\narr1.sort((a, b) => a.localeCompare(b)); // ['가', '나', '다', '바']\r\narr1.sort((a, b) => b.localeCompare(a)); // ['바', '다', '나', '가']\r\n```\r\n\r\n## 3. 배열 메서드\r\n\r\n```javascript\r\n\r\nlet Arr = [1,2,3,4,5]\r\n\r\n // 배열의 마지막에 새로운 요소를 추가 / 변경된 배열의 길이를 반환\r\nArr.push(12) // Arr = [1,2,3,4,5,12] / 6\r\n\r\n\t// 배열의 마지막 요소를 제거 / 한 후, 제거한 요소를 반환\r\n .pop() // Arr = [1,2,3,4] / 5\r\n\r\n // 배열의 첫 번째 자리에 새로운 요소를 추가/ 변경된 배열의 길이를 반환\r\n .unshift(12) // Arr = [12,1,2,3,4,5] / 6\r\n\r\n // 배열의 첫 번째 요소를 제거 / 제거한 요소를 반환\r\n .shift() // Arr = [2,3,4,5] / 1\r\n\r\n // 배열의 1번째 index부터 2개 원소 제거\r\n .splice(1, 2) // Arr = [1, 4, 5] / [2, 3] 제거됨\r\n\r\n // 배열 반전\r\n .reverse() 배열 반전 // [5,4,3,2,1]\r\n\r\n // 배열 계산\r\n //Arr.reduce((이전결과, 현재요소)=>이전결과+현재요소, 초기값)\r\n // 초기값이 없는 경우 acc = Arr[0] , cur = Arr[1] 이 된다.\r\n .reduce((acc,cur)=>acc+cur) 배열 계산 // 15\r\n\r\n // 배열 추출\r\n // .slice(x, y) x이상 y미만 index에 해당하는 배열 추출\r\n // 매개변수가 x밖에 없다면 x 이상 index에 해당하는 배열 추출\r\n // 매개변수가 음수인 경우 ex. (-2) 라면, 배열 끝에서 2번째 index 이상의 배열 추출\r\n .slice(0, 2) // [1,2]\r\n\r\n```\r\n"},{"title":"javascript basic 4 array 2","description":"javascript 기초 4 배열 다루기 - 2","category":"javascript","date":"2023-03-06","content":"\r\n# 고차함수와 메서드를 통해 JavaScript 배열 다뤄보기\r\n\r\n## 고차함수란?\r\n\r\n> 함수를 인자로 받거나, 함수를 반환함으로써 작동하는 함수로, .map() , .filter() , .forEach() , .reduce() 등이 있다.\r\n\r\n### 참고\r\n\r\n아래의 메서드들은 모두 원본 배열에 영향을 주지 않는다.\r\n\r\n또한 함수 안의 화살표 함수 arr.filter/map/...((element)=>(element>2)) 와 같이 괄호()를 이용하면 return을 해줄 필요가 없지만, arr.filter/map/...((element)=>{return element > 2}) 처럼 중괄호를 사용하면 return을 꼭 해줘야 한다.\r\n\r\n### Array.filter()\r\n\r\n특정 조건을 부합하는 값만 모아서 새로운 배열을 만들어준다.\r\n\r\n```javascript\r\n// ex\r\nconst arr = [\"a\", \"b\", \"b\", \"c\"];\r\nconst filtered = arr.filter(element => element != \"b\"); // arr의 원소는 b가 아닌 값으로 거른다.\r\nconsole.log(arr); // ['a', 'b', 'b', 'c']\r\nconsole.log(filtered); // ['a', 'c']\r\n\r\nconst arr_num = [0, 1, 2, 3, 4, 5];\r\nconst new_array = arr_num.filter(element => element > 3); // arr_num의 원소는 3을 초과한다\r\nconsole.log(new_array); // [ 4, 5 ]\r\n```\r\n\r\n### Array.map()\r\n\r\n반복문처럼 사용 가능. 원본 배열을 흐트러뜨리지 않고, 새로운 배열을 반환한다.\r\n\r\n단, key 값 같은 경우에는 값이 재할당되기 때문에 원본과 비교하는 의미가 없다. (ex. 리액트 사용 시)\r\n\r\n```javascript\r\nconst array_num = [0, 1, 2, 3, 4, 5]; // arr_n은 상수이지만\r\nconst new_arr_num = array_num.map(\r\n element => element * 10, // 요렇게 map으로 *10을 돌려주면\r\n);\r\nconsole.log(new_arr_num); // [ 0, 10, 20, 30, 40, 50 ] // 원본 배열 변경 없이 새 배열이 잘 만들어진다.\r\n```\r\n\r\n### Array.concat()\r\n\r\n두 배열을 합친다. 단, 중복 제거는 하지 않는다.\r\n\r\n```javascript\r\nconst array_num = [0, 1, 2, 3, 4, 5];\r\nconst new_array = [4, 5];\r\nconst merge = array_num.concat(new_array);\r\nconsole.log(merge); // [0, 1, 2, 3, 4, 5, 4, 5]\r\n```\r\n\r\n### Array.from()\r\n\r\n배열로 만들어준다.\r\n\r\n```javascript\r\nconst my_name = \"vihan9\";\r\nconst my_name_array = Array.from(my_name);\r\nconsole.log(my_name_array); // [ 'v', 'i', 'h', 'a', 'n', '9' ]\r\n\r\n// 조건을 넣어 배열을 만들고 싶다면? ex\r\nconst my_name_array = Array.from({ length: 4 }, (item, index) => index);\r\nconsole.log(my_name_array); // [ 0, 1, 2, 3 ] // length가 4인 배열을 만들어준다.\r\n\r\n// 어떤 배열을 index 값 배열로 바꾸고 싶다면? ex\r\nconst my_name = \"vihan9\";\r\nconst num_array = Array.from(my_name, (item, index) => index);\r\nconsole.log(num_array); // [ 0, 1, 2, 3, 4, 5 ]\r\n```\r\n\r\n### Array.find()\r\n\r\n숫자열 배열의 특정 값을 찾아주며, 조건에 맞는 첫 번째 값을 호출해준다.\r\n\r\n```javascript\r\nconst arr_num = [0, 1, 2, 3, 4, 5];\r\nconst find_num = arr_num.find(v => v > 3);\r\nconsole.log(find_num); // 4 // 3을 초과하는 첫 값인 4를 출력한다.\r\n```\r\n\r\n### indexOf()\r\n\r\n배열에서 지정된 요소를 찾을 수 있는 첫 번째 반환하고, 존재하지 않으면 -1을 반환한다.\r\n\r\n```javascript\r\nconst comment = \"오늘도 오류 없는 하루가 되게 해주세요\";\r\nconst searchStr = \"오류\";\r\nconst searchStr2 = \"해주세요\";\r\n\r\nconst indexOfFirst = comment.indexOf(searchStr);\r\nconst indexOfFirst2 = comment.indexOf(searchStr2);\r\n\r\nconsole.log(indexOfFirst); // 4 // 오류는 4번째 인덱스부터 시작한다.\r\nconsole.log(indexOfFirst2); // 17 // 해주세요는 17번째 인덱스부터 시작한다.\r\n```\r\n"},{"title":"javascript basic 5 loop","description":"javascript 기초 5 반복문 다루기","category":"javascript","date":"2023-03-06","content":"\r\n# for문 사용법\r\n\r\n## for문\r\n\r\n```javascript\r\n\r\nconst arr = [1,2,3,4,5,6,7,8,9,10];\r\n\r\nconst newArr = []\r\n (초기값(i=0) ; 조건(i가 10 미만까지 반복) ; 증감문(i는 1씩 증가한다.))\r\nfor(let i = 0;iconsole.log(element));\r\n\r\n// console.log\r\n1 2 3 4 5 6 7 8 9 10\r\n\r\n// 간단 예제\r\n\r\narr.forEach((element)=>console.log(element*10)); // 10 20 30 40 --- 80 90 100\r\n\r\n// for문처럼 화살표 함수 오른쪽 부분에 push 등과 같은 메서드를 통해 새 배열을 만들어줄 수도 있다.\r\nconst newArr = [];\r\narr.forEach((item)=>{newArr.push(item)});\r\n// newArr =\r\n[\r\n 1, 2, 3, 4, 5,\r\n 6, 7, 8, 9, 10\r\n]\r\n\r\n```\r\n\r\n## .map()\r\n\r\nforEach와 유사하게 배열 안의 각 요소들에게 콜백 함수를 적용시킨다.\r\n\r\n```javascript\r\nconst arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\r\n\r\nlet ex = arr.map(item => item * 10)[\r\n //console.log(ex)\r\n (10, 20, 30, 40, 50, 60, 70, 80, 90, 100)\r\n];\r\n\r\nconst arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\r\n\r\nlet ex = arr.map((item, index) => item * 10 + `(${index}번째)`)[\r\n //console.log(ex)\r\n (\"10(0번째)\",\r\n \"20(1번째)\",\r\n \"30(2번째)\",\r\n \"40(3번째)\",\r\n \"50(4번째)\",\r\n \"60(5번째)\",\r\n \"70(6번째)\",\r\n \"80(7번째)\",\r\n \"90(8번째)\",\r\n \"100(9번째)\")\r\n];\r\n```\r\n"},{"title":"nestjs custom repository","description":"Entityrepository()를 대신한 Repository 생성","category":"nestjs","date":"2023-03-04","content":"\r\n## 포스팅 시점 nestjs 버전 : 9.x.x\r\n\r\n새로운 토이 프로젝트를 만들면서 백엔드를 구축할 프레임워크로 nestjs를 이용하기로 마음먹었다.\r\n\r\n원래 사용하고 있던 javascript/typescript와 동일한 언어를 사용하기 때문에 백엔드 구축에 비교적 진입장벽이 낮다고 생각했기 때문에다.\r\n\r\n# nest 작동순서\r\n\r\nrequest -> controller -> service -> controller -> response 순으로 작동하나, DB와 관련된 일을 시키기 위해서는 repository를 생성해야한다고 한다. 즉,\r\n\r\nrequest -> controller -> service -> repo\\* -> service -> controller -> response\r\n로 한 단계가 추가된다. (Repostitory pattern)\r\n\r\n@EntityRepository를 통해 레포지토리를 설정해주는 방식이 많이 보였지만\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlkldE%2FbtrUQbTjKZA%2FA6hFzyRy8SsueBbUk8cTA1%2Fimg.png)\r\n\r\n현재 버전에서는 사용할 수 없어 다른 방법을 알아봤다.\r\n\r\n## 기본 설정\r\n\r\nconfig 설정\r\n\r\n```typescript\r\n// src/configs/typeorm.configs.ts\r\n\r\nexport const typeORMConfig: TypeOrmModuleOptions = {\r\n // Database Type\r\n type: \"postgres\", // 본인은 pg를 사용\r\n host: \"localhost\",\r\n port: 5432,\r\n username: \"postgres\",\r\n password: \"password\",\r\n database: \"database\",\r\n entities: [__dirname + \"/../**/*.entity.{js,ts}\"], // 해당 경로 + 엔티티이름.entity.{js.ts}로 된 엔티티를 이용\r\n // autoLoadEntities: true, // 위와 다르게 entity들 알아서 찾아줌\r\n synchronize: true, // @@@@@@@@ 배포할때 true 사용 시 데이터 삭제될 수 있음. production 단계에서는 필히 false로 해줄 것 권장 @@@@@@\r\n};\r\n```\r\n\r\nconfig 설정 후 root 모듈에 import\r\n\r\n```typescript\r\n// app.module.ts\r\n\r\nimport { Module } from \"@nestjs/common\";\r\nimport { AppController } from \"./app.controller\";\r\nimport { PostlistModule } from \"./postlist/postlist.module\";\r\nimport { typeORMConfig } from \"./configs/typeorm.configs\";\r\nimport { TypeOrmModule } from \"@nestjs/typeorm/dist\";\r\n\r\n@Module({\r\n imports: [TypeOrmModule.forRoot(typeORMConfig), PostlistModule],\r\n controllers: [AppController],\r\n providers: [],\r\n})\r\nexport class AppModule {}\r\n```\r\n\r\nentity 생성\r\n\r\n```typescript\r\n// src/postlist/entities/postlist.entity.ts\r\n\r\nimport { BaseEntity, Column, PrimaryGeneratedColumn, Entity } from \"typeorm\";\r\n\r\n@Entity()\r\nexport class Postlist extends BaseEntity {\r\n @PrimaryGeneratedColumn()\r\n id: number;\r\n\r\n @Column()\r\n title: string;\r\n\r\n @Column()\r\n content: string;\r\n\r\n @Column()\r\n nickname: string;\r\n\r\n @Column()\r\n imageUrl?: string;\r\n}\r\n```\r\n\r\n> config 설정 시 entities에 이름을 ~~.entity.{js.ts} 로 설정해놓고, 정작 entity 파일 이름을 설정한 형식과 다르게 작성하면 [EntityMetadataNotFound] 에러를 띄우게 되기 때문에 설정한대로 이름을 지어줘야 한다.\r\n\r\n참조 : https://github.com/typeorm/typeorm/issues/1327\r\n\r\n## custumRepository 만들기\r\n\r\n본인은 src에 database 디렉토리를 만들어, 데코레이터와 모듈을 그 안에 넣어줬다.\r\n\r\n```typescript\r\n// src/database/typeorm-ex.decorator.ts\r\n\r\nimport { SetMetadata } from \"@nestjs/common\";\r\n\r\nexport const TYPEORM_EX_CUSTOM_REPOSITORY = \"TYPEORM_EX_CUSTOM_REPOSITORY\";\r\n\r\nexport function CustomRepository(entity: Function): ClassDecorator {\r\n return SetMetadata(TYPEORM_EX_CUSTOM_REPOSITORY, entity);\r\n}\r\n\r\n// typeorm-ex.module.ts\r\n\r\nimport { DynamicModule, Provider } from \"@nestjs/common\";\r\nimport { getDataSourceToken } from \"@nestjs/typeorm\";\r\nimport { DataSource } from \"typeorm\";\r\nimport { TYPEORM_EX_CUSTOM_REPOSITORY } from \"./typeorm-ex.decorator\";\r\n\r\nexport class TypeOrmExModule {\r\n public static forCustomRepository any>(\r\n repositories: T[],\r\n ): DynamicModule {\r\n const providers: Provider[] = [];\r\n\r\n for (const repository of repositories) {\r\n const entity = Reflect.getMetadata(\r\n TYPEORM_EX_CUSTOM_REPOSITORY,\r\n repository,\r\n );\r\n\r\n if (!entity) {\r\n continue;\r\n }\r\n\r\n providers.push({\r\n inject: [getDataSourceToken()],\r\n provide: repository,\r\n useFactory: (dataSource: DataSource): typeof repository => {\r\n const baseRepository = dataSource.getRepository(entity);\r\n return new repository(\r\n baseRepository.target,\r\n baseRepository.manager,\r\n baseRepository.queryRunner,\r\n );\r\n },\r\n });\r\n }\r\n\r\n return {\r\n exports: providers,\r\n module: TypeOrmExModule,\r\n providers,\r\n };\r\n }\r\n}\r\n```\r\n\r\n## 사용할 repository 생성\r\n\r\n```typescript\r\n// src/postlist/repository/postlist.repository.ts\r\n\r\nimport { CustomRepository } from \"src/database/typeorm-ex.decorator\";\r\nimport { Repository } from \"typeorm\";\r\nimport { Postlist } from \"../entities/postlist.entity\";\r\n\r\n@CustomRepository(Postlist)\r\nexport class PostlistRepository extends Repository {}\r\n```\r\n\r\n## module에 생성한 repository import\r\n\r\n```typescript\r\n// src/postlist/postlist.module.ts\r\n\r\nimport { Module } from \"@nestjs/common\";\r\nimport { PostlistController } from \"./postlist.controller\";\r\nimport { PostlistService } from \"./postlist.service\";\r\nimport { PostlistRepository } from \"./repository/postlist.repository\";\r\nimport { TypeOrmExModule } from \"src/database/typeorm-ex.module\";\r\n\r\n@Module({\r\n imports: [TypeOrmExModule.forCustomRepository([PostlistRepository])],\r\n controllers: [PostlistController],\r\n providers: [PostlistService],\r\n})\r\nexport class PostlistModule {}\r\n```\r\n\r\n### Module에 import 후 service 비즈니스 로직에만 repository를 넣어주면 된다.\r\n\r\n```typescript\r\n// src/postlist/postlist.service.ts\r\n\r\nimport { Injectable, NotFoundException } from \"@nestjs/common\";\r\nimport { UpdatePostDto } from \"./dto/update-postlist.dto\";\r\n\r\nimport { PostlistRepository } from \"./repository/postlist.repository\";\r\nimport { Postlist } from \"./entities/postlist.entity\";\r\nimport { CreatePostDto } from \"./dto/create-postlist.dto\";\r\n\r\n@Injectable()\r\nexport class PostlistService {\r\n constructor(private readonly postlistRepository: PostlistRepository) {}\r\n\r\n async getAllPost(): Promise {\r\n const found = await this.postlistRepository.find();\r\n return found;\r\n }\r\n\r\n async getPostById(id: number): Promise {\r\n const found = await this.postlistRepository.findOne({ where: { id: id } });\r\n if (!found) {\r\n throw new NotFoundException(`Cannot find post with id ${id}`);\r\n }\r\n return found;\r\n }\r\n}\r\n\r\n// src/postlist/postlist.controller.ts\r\n\r\nimport { Body, Controller, Get, Param, Patch, Post } from \"@nestjs/common\";\r\nimport { CreatePostDto } from \"./dto/create-postlist.dto\";\r\nimport { PostlistService } from \"./postlist.service\";\r\nimport { UpdatePostDto } from \"./dto/update-postlist.dto\";\r\nimport { Delete } from \"@nestjs/common/decorators\";\r\nimport { Postlist } from \"./entities/postlist.entity\";\r\n\r\n@Controller(\"postlist\")\r\nexport class PostlistController {\r\n constructor(readonly postlistService: PostlistService) {}\r\n\r\n @Post()\r\n createOne(@Body() createPostDto: CreatePostDto): Promise {\r\n return this.postlistService.createPost(createPostDto);\r\n }\r\n\r\n @Get()\r\n getAll(): Promise {\r\n return this.postlistService.getAllPost();\r\n }\r\n\r\n @Get(\":id\")\r\n getOne(@Param(\"id\") id: number): Promise {\r\n return this.postlistService.getPostById(id);\r\n }\r\n\r\n @Patch(\":id\")\r\n updateOne(\r\n @Param(\"id\") id: number,\r\n @Body() updateData: UpdatePostDto,\r\n ): Promise {\r\n return this.postlistService.updatePost(id, updateData);\r\n }\r\n\r\n @Delete(\":id\")\r\n deleteOne(@Param(\"id\") id: number): Promise {\r\n return this.postlistService.deletePost(id);\r\n }\r\n}\r\n```\r\n\r\n필요한 로직을 설정한 후 postman을 작동시켜주면 정상적으로 실행되는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMxspb%2FbtrUIZG6vWP%2F0dVetFuaUfkfuI9EXCKeN0%2Fimg.png)\r\n\r\nnextjs와 동시에 작업하기 위해 nestjs 포트를 3001로 설정했지만, 보통은 nest 설치 시 app에 3000으로 설정된다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOfHG5%2FbtrUOQBYHjX%2FoGOqOJtiF2KbPiyKu9JCdK%2Fimg.png)\r\n\r\nDB는 postgreSQL을 이용했다.\r\n정상적으로 테이블에 있는 데이터가 추출되는 것이 잘 확인된다.\r\n\r\n### 참고 :\r\n\r\nhttps://orkhan.gitbook.io/typeorm/readme_ko\r\n\r\nhttps://docs.nestjs.com/techniques/database\r\n"},{"title":"nextjs loading","description":"nextjs에서의 로딩처리","category":"nextjs","date":"2023-03-04","content":"\r\n# Nextjs에서 로딩처리하기\r\n\r\n### 모든 페이지를 미리 렌더링하는 NextJS 특성상, 다른 페이지로 라우팅이 진행될때, 사용자는 가만히 멈춰있는 화면을 보게될 수 있다. 때문에 페이지 전환을 정지화면으로 두지 않기 위해 로딩 처리를 구현해볼 수 있다.\r\n\r\n## 로딩 스피너 세팅\r\n\r\n```typescript\r\n// _app.tsx\r\n\r\n// 미리 만들어놓은 로딩 훅과 로딩 스피너\r\nimport { useLoading } from \"src/hooks/useLoading\";\r\nimport { LoadingSpinner } from \"src/components/videos/video/LoadingSpinner\";\r\n\r\n\r\n {isLoading ? : null}\r\n \r\n;\r\n```\r\n\r\n로딩스피너는 CSS로 만들어도 좋고, SVG나 GIF, 라이브러리 등 자유롭게 적용해도 좋다.\r\n\r\n## 라우팅 시 적용될 이벤트 설정\r\n\r\n```typescript\r\n// useLoading.ts\r\n\r\nimport Router from \"next/router\";\r\nimport { useEffect, useState } from \"react\";\r\n\r\nexport const useLoading = () => {\r\n const [nowLoading, setNowLoading] = useState(false);\r\n useEffect(() => {\r\n const start = () => {\r\n setNowLoading(true);\r\n };\r\n const end = () => {\r\n setNowLoading(false);\r\n };\r\n Router.events.on(\"routeChangeStart\", start);\r\n Router.events.on(\"routeChangeComplete\", end);\r\n Router.events.on(\"routeChangeError\", end);\r\n return () => {\r\n Router.events.off(\"routeChangeStart\", start);\r\n Router.events.off(\"routeChangeComplete\", end);\r\n Router.events.off(\"routeChangeError\", end);\r\n };\r\n }, []);\r\n\r\n return nowLoading ? true : false;\r\n};\r\n```\r\n\r\n- routeChangeStart(url, { shallow }) - 라우트가 변경되기 시작할때 트리거됨.\r\n\r\n- routeChangeComplete(url, { shallow }) - 라우트가 완전히 변경되었을 때 트리거됨.\r\n\r\n- routeChangeError(err, url, { shallow }) - 라우트 변경 중에 에러가 발생했거나, 취소되었을 때 트리거됨.\r\n\r\n\\_app.tsx(jsx)에 로딩 스피너나 로딩 페이지를 적용해 놓으면,\r\n\r\n라우팅으로 인한 페이지 이동이 일어날 때마다 원하는 로딩 창을 호출해줄 수 있다.\r\n\r\n## 적용된 페이지\r\n\r\n![image](https://blog.kakaocdn.net/dn/cWz72g/btrSmvngm4A/hgKg4FviDpAqQk5U6kGHXK/img.gif)\r\n"},{"title":"nextjs react responsive","description":"nextjs에 react responsive 적용하기","category":"nextjs","date":"2023-03-04","content":"\r\n### 이번에 새로 만들고 있는 토이 프로젝트를 작업하던 중, 모바일 화면일때만 화면을 보여주고 싶었다. 단, 모든 컴포넌트에 미디어쿼리를 통해 작성해줄 수는 없기에 react-responsive를 적용해 화면이 481px을 넘어가면 고정된 페이지를 보여줄 수 있도록 코드를 작성했다.\r\n\r\n## react-responsive 설치\r\n\r\n```bash\r\nyarn add react-responsive\r\n```\r\n\r\n## hook 설정\r\n\r\n```typescript\r\n// hook\r\n// useMideaQuery.tsx\r\nexport const Desktop = ({ children }: any) => {\r\n const isDesktop = useMediaQuery({\r\n minWidth: 481,\r\n });\r\n return isDesktop ? children : null;\r\n};\r\n\r\nexport const Mobile = ({ children }: any) => {\r\n const isMobile = useMediaQuery({\r\n minWidth: 481,\r\n });\r\n return Mobile ? children : null;\r\n};\r\n```\r\n\r\n## \\_app.ts 적용\r\n\r\n```typescript\r\n// _app.tsx\r\n\r\n// ...\r\nimport { Mobile } from \"src/hooks/useMideaQuery\";\r\nimport { CannotDesktop } from \"src/desktop/CannotDesktop\";\r\n\r\n<>\r\n \r\n \r\n {isLoading ? : null}\r\n \r\n \r\n \r\n \r\n \r\n \r\n;\r\n```\r\n\r\n똑같은 코드로 리액트 코드에 적용할 때는 문제가 없었지만, Nextjs를 이용한 이번 상황의 경우 Hydration 에러가 발생했다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjYUXg%2FbtrTRCeaovE%2FwQr4OZdJectnkkG06XmTEk%2Fimg.png)\r\n\r\nvercel로 배포할 때는 정상적으로 빌드되기는 했지만\r\n\r\n로컬에서 작업할때 지속적으로 발생하는 오류 메세지 창이 불편하기도 하여 바로 처리해봤다.\r\n\r\n> Hydration failed because the initial UI does not match what was rendered on the server\r\n\r\n초기 UI가 서버에서 렌더링된 것과 일치하지 않는다고 한다.\r\n\r\nhttps://nextjs.org/docs/messages/react-hydration-error\r\n\r\n공식 페이지를 보면 \"애플리케이션을 렌더링하는 동안 미리 렌더링된 React 트리(SSR/SSG)와 브라우저에서 첫 번째 렌더링 중에 렌더링된 React 트리 간에 차이가 있었기 때문에 발생한 오류\"라고 한다.\r\n\r\n일반적으로 이 문제는 사전 렌더링과 브라우저 간에 다를 수 있는 항목에 의존하는 특정 라리브러리 또는 애플리케이션 코드를 사용하여 발생한다고 하는데,\r\n\r\n나의 경우, react-responsive에 의한 경우였다.\r\n\r\n## 코드 수정\r\n\r\n```typescript\r\n// useMideaQuery.tsx\r\n\r\nimport { useEffect, useState } from \"react\";\r\nimport { useMediaQuery } from \"react-responsive\";\r\n\r\nexport const Mobile = () => {\r\n const [mobile, setMobile] = useState(false);\r\n const isMobile = useMediaQuery({ maxWidth: 480 });\r\n\r\n const checkResize = () => {\r\n if (isMobile) {\r\n setMobile(true);\r\n } else {\r\n setMobile(false);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n checkResize();\r\n }, [isMobile]);\r\n\r\n return mobile;\r\n};\r\n//_app.tsx\r\n\r\n\r\n {isMobile ? (\r\n \r\n {isLoading ? : null}\r\n \r\n \r\n ) : (\r\n \r\n )}\r\n;\r\n```\r\n\r\nuseMideaQuery가 window 화면을 기준으로 동작하기 때문에,\r\n\r\n공식문서의 예제와 비슷하게 useState를 통한 boolean 값으로 화면을 노출시켜줬다.\r\n\r\n위의 방식을 적용하니 hydration 에러 없이 잘 작동하는 모습을 볼 수 있었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdH0CZg%2FbtrTRp0izJZ%2FiolwpQXLvGOommEBhpCkaK%2Fimg.png)\r\n"},{"title":"redux mock server","description":"postman을 이용해 redux Mock server(json-server) 사용해보기","category":"redux","date":"2023-03-04","content":"\r\n### React를 사용하면서 서버와 연결 전, Post Man을 통해 임시로 서버를 연결해 React와 소통하는 데이터와 view가 어떻게 처리되는지 확인하고 싶을때 사용해볼 수 있다.\r\n\r\npostman 링크 : https://www.postman.com/\r\n\r\n## json-server 설치\r\n\r\n```bash\r\nyarn add json-server\r\n```\r\n\r\n위 명령어 입력 시 db.json 파일이 자동으로 생성된다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fle0HO%2FbtrMjcfRkOG%2FPMsBpdgzHf6545oXRlN8BK%2Fimg.png)\r\n\r\n설치가 완료된 후 아래의 명령어를 입력하면 json server가 실행된다.\r\n\r\n## json-server 실행\r\n\r\n```bash\r\nyarn json-server --watch db.json --port 3001\r\n```\r\n\r\n> json-server를 설치해줄때 생성된 db.json 파일을 실행하는데, 3001번 포트를 사용한다는 의미이다. 포트를 설정해주지 않으면 기본값은 3000이다.\r\n\r\n> 보통 React를 로컬로 실행시킬때의 포트도 3000이기 때문에 3001번으로 열어줬다. 포트번호는 3001이든 4000이든 5000이든 사용하고 있는 포트번호만 아니면 된다.\r\n\r\n## postman 사용\r\n\r\n이제 임시 DB를 사용하고 있기 때문에 Post Man을 설치하거나, 웹에 로그인을 한 후, json-server가 실행된 상태로 post man에 적용하고자 하는 url을 입력해주면 GET, POST, PUT, DELETE 등 다양한 요청을 처리할 수 있게 된다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl4yDj%2FbtrMkdzirLp%2FweLBKktEnHFX5QcT4NHTK1%2Fimg.png)\r\n\r\n사용되는 url은 db.json에 있는 목록명을 기준으로 적용된다.\r\n\r\n```javascript\r\n\r\n// ex\r\n\r\n{\r\n \"comments\": [\r\n {\r\n \"id\": 1,\r\n \"profile_url\": \"https://picsum.photos/id/1/50/50\",\r\n \"author\": \"abc_1\",\r\n \"content\": \"UI 테스트는 어떻게 진행하나요\",\r\n \"createdAt\": \"2022-03-01\"\r\n },\r\n {\r\n \"id\": 2,\r\n \"profile_url\": \"https://picsum.photos/id/2/50/50\",\r\n \"author\": \"abc_2\",\r\n \"content\": \"막히면 대답은 빨리 해주나요\",\r\n \"createdAt\": \"2022-03-02\"\r\n },\r\n ],\r\n \"post\": [\r\n {\r\n \"id\": 1,\r\n \"author\": \"abc_1\",\r\n \"content\": \"UI 테스트는 어떻게 진행하나요\",\r\n \"createdAt\": \"2022-03-01\"\r\n }\r\n {\r\n \"id\": 2,\r\n \"author\": \"abc_2\",\r\n \"content\": \"막히면 대답은 빨리 해주나요\",\r\n \"createdAt\": \"2022-03-01\"\r\n }\r\n ]\r\n}\r\n\r\n```\r\n\r\n예를 들어 db.json이 위와 같이 돼있는 상태라면\r\n\r\n### http://localhost:3001/post - post 데이터\r\n\r\n1. GET : http://localhost:3001/post - 전체 post 목록 불러오기\r\n2. GET : http://localhost:3001/post/1 - id가 1인 post 불러오기\r\n3. POST : http://localhost:3001/post - 새 post 생성(저장)\r\n4. PUT : http://localhost:3001/post/1 - id가 1인 post 수정하기\r\n5. DELETE : http://localhost:3001/post/1 - id가 1인 post 삭제하기\r\n\r\n### http://localhost:3001/comments - comments 데이터\r\n\r\n1. GET : http://localhost:3001/comments - 전체 comments 목록 불러오기\r\n2. GET : http://localhost:3001/comments/1 - id가 1인 comments 불러오기\r\n3. POST : http://localhost:3001/comments - 새 comments 생성(저장)\r\n4. PUT : http://localhost:3001/comments/1 - id가 1인 comments 수정하기\r\n5. DELETE : http://localhost:3001/comments/1 - id가 1인 comments 삭제하기\r\n\r\n위와 같은 방식으로 기본적인 동작을 사용해볼 수 있고, header 값이나 body 값 설정 등 다양한 작업을 진행해볼 수 있다.\r\n\r\nRedux를 사용할때, 해당 데이터들을 추출하여 전역 상태로 관리해보는 등의 작업을 실행할 수 있다.\r\n"},{"title":"redux toolkit async thunk","description":"React reduxToolkit과 미들웨어 AsyncThunk 사용해보기","category":"redux","date":"2023-03-04","content":"\r\n# ReduxToolkit\r\n\r\n리덕스 툴킷은 일반적인 리덕스보다 store 설정을 용이하게 해주고, 추가적인 패키지 설치가 필요하지 않게 설계되었으며, 보일러플레이트(여러 군데에서 반복되는 코드)를 최소화하기 위해 만들어졌다.\r\n\r\n더 사용하기 쉽다는 의미이다. 이번 글에서는 미들웨어인 AsyncThunk와 함께 사용하는 방법을 확인할 수 있다.\r\n\r\n## 1. 설치\r\n\r\n```bash\r\nyarn add @reduxjs/toolkit react-redux\r\n```\r\n\r\n## 2. store 생성\r\n\r\n```javascript\r\n// src/redux/store.js\r\n\r\nimport { configureStore } from \"@reduxjs/toolkit\";\r\nimport posts from \"./modules/postSlice\"; // 만들어줄 Slice\r\n\r\nconst store = configureStore({\r\n reducer: {\r\n posts,\r\n },\r\n});\r\nexport default store;\r\n```\r\n\r\n## 3. 최상위 파일(index.js, App.js 등)에 Provider import해오기\r\n\r\n```javascript\r\n// import -- 기존 코드들 --\r\nimport { Provider } from \"react-redux\";\r\nimport store from \"./redux/store\";\r\n\r\nconst root = ReactDOM.createRoot(document.getElementById(\"root\"));\r\nroot.render(\r\n \r\n \r\n ,\r\n);\r\n```\r\n\r\n## 4. 슬라이스 생성\r\n\r\n2번의 store에 import 해줄 파일이다.\r\n\r\n```javascript\r\n// postSlice.js\r\n\r\nimport { createAsyncThunk, createSlice } from \"@reduxjs/toolkit\";\r\nimport axios from \"axios\";\r\n\r\nconst BASE_URL = \"http://localhost:3001\";\r\n\r\nconst initialState = {\r\n // data, isLoading, error로 상태관리\r\n posts: [],\r\n isLoading: false,\r\n error: null,\r\n};\r\n\r\nexport const getPosts = createAsyncThunk(\r\n \"GET_ALL_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n const { data } = await axios.get(`${BASE_URL}/posts`);\r\n return thunkAPI.fulfillWithValue(data);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const addPosts = createAsyncThunk(\r\n \"POST_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n const { data } = await axios.post(`${BASE_URL}/posts`, payload);\r\n console.log(\"data\", data);\r\n return thunkAPI.fulfillWithValue(data);\r\n } catch (errer) {\r\n return thunkAPI.rejectWithValue(errer);\r\n }\r\n },\r\n);\r\n\r\nexport const updatePosts = createAsyncThunk(\r\n \"UPDATAE_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n console.log(payload);\r\n const { data } = await axios.put(\r\n `${BASE_URL}/posts/${payload.id}`,\r\n payload,\r\n );\r\n console.log(\"data\", DataTransfer);\r\n return thunkAPI.fulfillWithValue(data.data);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const deletePosts = createAsyncThunk(\r\n \"DELETE_posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n await axios.delete(`${BASE_URL}/posts/${payload}`);\r\n return thunkAPI.fulfillWithValue(payload);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const postsSlice = createSlice({\r\n name: \"posts\",\r\n initialState,\r\n reducers: {},\r\n extraReducers: {\r\n /* Pending */\r\n [getPosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n [addPosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n [deletePosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n /* Fulfilled */\r\n [getPosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n console.log(action);\r\n state.posts = [...action.payload];\r\n },\r\n [addPosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n state.posts.push(action.payload);\r\n },\r\n [updatePosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n console.log(action);\r\n const newState = state.posts.map(item =>\r\n action.meta.arg.id === item.id\r\n ? { ...item, content: action.meta.arg.content }\r\n : item,\r\n );\r\n state.posts = newState;\r\n return state;\r\n },\r\n [deletePosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n const newState = state.posts.filter(item => item.id !== action.meta.arg);\r\n state.posts = newState;\r\n return state;\r\n },\r\n /* Rejected */\r\n [getPosts.rejected]: (state, action) => {\r\n state.isLoading = false;\r\n state.error = action.payload;\r\n },\r\n },\r\n});\r\n\r\nexport default postsSlice.reducer;\r\n```\r\n\r\n이 상태에서 Postman을 통해 json 서버를 이용하기 때문에 BASE_URL은 로컬 경로로 설정해줬다.\r\n\r\nextraReducers 의 pending, fulfilled, rejected는 각각 대기중/성공/실패 정도로 생각하면 된다.\r\n\r\n아래와 같이 PostMan을 통해 db.json에 데이터가 정상적으로 확인되면\r\n\r\n포스트맨 사용법 : https://lee-yo-han.github.io/redux/redux와-mock-server-사용하기\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fri6Yi%2FbtrMjiBl1Va%2FlCNsQyz5vPTIhIfBFfpC41%2Fimg.png)\r\n\r\n아래와 같이 Redux hook을 통해 데이터를 자유롭게 사용할 수 있다.\r\n\r\n```javascript\r\nimport { getPosts } from \"../redux/modules/postSlice\";\r\nimport { useEffect } from \"react\";\r\nimport { useDispatch, useSelector } from \"react-redux\"; // Redux hooks\r\n\r\nexport default function Home() {\r\n const data = useSelector(state => state.posts.posts); // postSlice에 있는 전역 State 가져오기\r\n const dispatch = useDispatch(); //dispatch 사용 준비\r\n\r\n useEffect(() => {\r\n dispatch(getPosts()); // dispatch 사용 dispatch(액션함수())\r\n }, []);\r\n\r\n console.log(data);\r\n\r\n return (\r\n
\r\n

redux랑 포스팅CRUD

\r\n

데이터를 주세욥

\r\n
    \r\n {data.map(item => (\r\n
  • \r\n {item.id}\r\n {item.nickname}\r\n {item.content}\r\n
  • \r\n ))}\r\n
\r\n
\r\n );\r\n}\r\n```\r\n\r\n위의 코드에서는 액션 함수의 인자(괄호())가 비어있지만, Slice에 원하는 액션 함수들을 작성한 후 dispatch를 해줄때 데이터가 필요한 경우는 아래의 순서로 데이터가 처리된다고 생각하면 좋다.\r\n\r\n### 데이터 처리 순서\r\n\r\n```javascript\r\n\r\n// jsx component\r\ndispath(액션함수(id등의 데이터))\r\n\r\n// => postSlice 파일 내의 액션 함수\r\nexport const addPosts = createAsyncThunk(\r\n \"POST_Posts\",\r\n async (payload, thunkAPI) => { // payload를 통해 데이터를 받고 아래 코드에서 처리할 수 있도록 함\r\n try {\r\n const { data } = await axios.post(`${BASE_URL}/posts`, payload);\r\n console.log(\"data\", data);\r\n return thunkAPI.fulfillWithValue(data); // 요청 성공 부분\r\n } catch (errer) {\r\n return thunkAPI.rejectWithValue(errer);\r\n }\r\n }\r\n );\r\n\r\n// => extraReducers\r\nexport const postsSlice = createSlice({\r\n name: \"posts\",\r\n initialState,\r\n reducers: {},\r\n extraReducers: {\r\n // ---- 생략 ----\r\n /* Fulfilled */\r\n [addPosts.fulfilled]: (state, action) => { // 요청 성공 시, action 인자를 통해 state(전역 상태)를 관리해줄 수 있음 (ex action.payload .---)\r\n state.isLoading = false;\r\n state.posts.push(action.payload);\r\n },\r\n // ---- 생략 ----\r\n```\r\n\r\n데이터가 정상적으로 화면으로 출력되는 것을 볼 수 있다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKVbGZ%2FbtrMljZJHmV%2F3XHhLVMqFgXEAjz2HqKky0%2Fimg.png)\r\n\r\n단, useSelector를 사용할 때, (state)=>state... 부분은 데이터에 따라 값의 구조가 다를 수 있기 때문에, console.log()를 통해 데이터를 어떻게 받아오는지 확인하며 코드를 작성해주자.\r\n\r\n## 간단한 CRUD 적용 예시\r\n\r\n![image](https://blog.kakaocdn.net/dn/bDrG2e/btrMjWLhmxB/gsemxzlV7oyCzREe8oHOM1/img.gif)\r\n\r\n## AsyncThunk를 사용한다는 것은\r\n\r\n공식문서 : https://redux-toolkit.js.org/api/createAsyncThunk\r\n\r\n공식 문서에 반환된 Promise를 기반으로 LifeCycle 작업을 전달하는 Thunk 작업 생성자를 반환해 비동기 요청 LifeCycle을 처리하기 위한 표준 권장 접근 방식을 추상화한다는 내용이 있다\r\n\r\n이것은 위의 데이터 처리 순서로 설명한 것과 유사하다.\r\n\r\n```javascript\r\n\r\nexport const addPosts = createAsyncThunk(\r\n \"POST_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n const { data } = await axios.post(`${BASE_URL}/posts`, payload);\r\n console.log(\"data\", data);\r\n return thunkAPI.fulfillWithValue(data);\r\n } catch (errer) {\r\n return thunkAPI.rejectWithValue(errer);\r\n }\r\n }\r\n );\r\n\r\n ↑↑↑ 이 부분에서 Promise를 반환해주고\r\n\r\n\r\n\r\n\r\n ↓↓↓↓ 이 부분에서 대기/성공/실패 로 구분해 데이터를 처리해준다\r\n\r\n //Panding\r\n //Fulfilled\r\n ...\r\n\r\n [addPosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n state.posts.push(action.payload); // 게시글을 추가했을때 처리해주는 방식\r\n },\r\n\r\n ...\r\n //Rejected\r\n\r\n```\r\n\r\n# 전체 예제코드\r\n\r\n```javascript\r\n// Home.jsx\r\n\r\nimport {\r\n deletePosts,\r\n getPosts,\r\n updatePosts,\r\n addPosts,\r\n} from \"../redux/modules/postSlice\";\r\n\r\nimport { useState, useEffect } from \"react\";\r\nimport { useDispatch, useSelector } from \"react-redux\";\r\n\r\nexport default function Home() {\r\n const [inputValue, setInputValue] = useState();\r\n\r\n const data = useSelector(state => state.posts.posts);\r\n const dispatch = useDispatch();\r\n useEffect(() => {\r\n dispatch(getPosts());\r\n }, []);\r\n console.log(data);\r\n\r\n const addButton = () => {\r\n let addData = {\r\n nickname: \"한삐\",\r\n content: inputValue,\r\n };\r\n dispatch(addPosts(addData));\r\n };\r\n\r\n const deletButton = props => {\r\n dispatch(deletePosts(props));\r\n };\r\n\r\n const editButton = props => {\r\n let updateData = {\r\n id: props,\r\n nickname: \"한삐\",\r\n content: \"냠냠1231241214\",\r\n };\r\n dispatch(updatePosts(updateData));\r\n };\r\n\r\n return (\r\n
\r\n

redux랑 포스팅CRUD

\r\n

데이터를 주세욥

\r\n setInputValue(e.target.value)} type=\"text\" />\r\n \r\n
    \r\n {data.map(item => (\r\n
  • \r\n
    \r\n {item.id}\r\n
    \r\n {item.nickname}\r\n
    \r\n {item.content}\r\n
    \r\n
    \r\n
    \r\n \r\n \r\n
    \r\n
  • \r\n ))}\r\n
\r\n
\r\n );\r\n}\r\n```\r\n\r\n```javascript\r\n// store.js\r\nimport { configureStore } from \"@reduxjs/toolkit\";\r\nimport posts from \"./modules/postSlice\";\r\n\r\nconst store = configureStore({\r\n reducer: {\r\n posts,\r\n },\r\n});\r\n\r\nexport default store;\r\n```\r\n\r\n```javascript\r\n// postSlice.js\r\n\r\nimport { createAsyncThunk, createSlice } from \"@reduxjs/toolkit\";\r\nimport { current } from \"@reduxjs/toolkit\";\r\nimport axios from \"axios\";\r\n\r\nconst BASE_URL = \"http://localhost:3001\";\r\n\r\nconst initialState = {\r\n // data, isLoading, error로 상태관리\r\n posts: [],\r\n isLoading: false,\r\n error: null,\r\n};\r\n\r\nexport const getPosts = createAsyncThunk(\r\n \"GET_ALL_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n const { data } = await axios.get(`${BASE_URL}/posts`);\r\n return thunkAPI.fulfillWithValue(data);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const addPosts = createAsyncThunk(\r\n \"POST_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n const { data } = await axios.post(`${BASE_URL}/posts`, payload);\r\n console.log(\"data\", data);\r\n return thunkAPI.fulfillWithValue(data);\r\n } catch (errer) {\r\n return thunkAPI.rejectWithValue(errer);\r\n }\r\n },\r\n);\r\n\r\nexport const updatePosts = createAsyncThunk(\r\n \"UPDATAE_Posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n console.log(payload);\r\n const { data } = await axios.put(\r\n `${BASE_URL}/posts/${payload.id}`,\r\n payload,\r\n );\r\n console.log(\"data\", DataTransfer);\r\n return thunkAPI.fulfillWithValue(data.data);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const deletePosts = createAsyncThunk(\r\n \"DELETE_posts\",\r\n async (payload, thunkAPI) => {\r\n try {\r\n await axios.delete(`${BASE_URL}/posts/${payload}`);\r\n return thunkAPI.fulfillWithValue(payload);\r\n } catch (error) {\r\n return thunkAPI.rejectWithValue(error);\r\n }\r\n },\r\n);\r\n\r\nexport const postsSlice = createSlice({\r\n name: \"posts\",\r\n initialState,\r\n reducers: {},\r\n extraReducers: {\r\n /* Pending */\r\n [getPosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n [addPosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n [deletePosts.pending]: (state, action) => {\r\n state.isLoading = true;\r\n },\r\n /* Fulfilled */\r\n [getPosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n console.log(action);\r\n state.posts = [...action.payload];\r\n },\r\n [addPosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n state.posts.push(action.payload);\r\n },\r\n [updatePosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n console.log(action);\r\n const newState = state.posts.map(item =>\r\n action.meta.arg.id === item.id\r\n ? { ...item, content: action.meta.arg.content }\r\n : item,\r\n );\r\n state.posts = newState;\r\n return state;\r\n },\r\n [deletePosts.fulfilled]: (state, action) => {\r\n state.isLoading = false;\r\n const newState = state.posts.filter(item => item.id !== action.meta.arg);\r\n state.posts = newState;\r\n return state;\r\n },\r\n /* Rejected */\r\n [getPosts.rejected]: (state, action) => {\r\n state.isLoading = false;\r\n state.error = action.payload;\r\n },\r\n },\r\n});\r\n\r\nexport default postsSlice.reducer;\r\n```\r\n"},{"title":"redux typescript react reduxtoolkit","description":"Typescript react에서 ReduxToolkit 사용해보기","category":"redux","date":"2023-03-04","content":"\r\njavascript에서의 ReduxToolkit 사용 예제 : https://lee-yo-han.github.io/redux/redux-toolkit-사용해보기\r\n\r\n## 1. 설치\r\n\r\n```bash\r\nyarn add @reduxjs/toolkit react-redux\r\n```\r\n\r\n## 2. store 설정\r\n\r\nstate와 dispatch type 생성\r\n\r\n```typescript\r\n// store.ts\r\nimport { configureStore } from \"@reduxjs/toolkit\";\r\nimport commentSlice from \"./commentSlice\";\r\n\r\nconst store = configureStore({\r\n reducer: {\r\n commentSlice,\r\n },\r\n});\r\nexport default store;\r\n\r\nexport type RootState = ReturnType; // 1. state type 가져오기\r\nexport type AppDispatch = typeof store.dispatch; // 2. dispatch type 가져오기\r\n```\r\n\r\n## 3. Redux hook 설정\r\n\r\ndispatch 함수와 useSelector 실행을 위해, Redux hook을 따로 설정해준다.\r\n\r\n```typescript\r\n// useRedux.ts\r\n\r\nimport type { TypedUseSelectorHook } from \"react-redux\";\r\nimport type { RootState, AppDispatch } from \"../redux/store\"; // store에서 미리 설정해준 state와 dispatch type\r\n\r\nexport const useAppDispatch: () => AppDispatch = useDispatch;\r\nexport const useAppSelector: TypedUseSelectorHook = useSelector;\r\n```\r\n\r\n위의 설정을 마치면 javascript에서 dispatch를 사용하는 것과 같이 사용할 수 있다.\r\n\r\n```typescript\r\n\r\n// Form.tsx\r\nimport { useAppDispatch, useAppSelector } from \"../hook/useRedux\";\r\nimport { FormEvent } from \"../type\"; // 별도로 지정한 eventType\r\nimport { addComments } from \"../redux/commentSlice\"; // Slice의 action함수\r\n\r\nexport const Form = () => {\r\n\tconst dispatch = useAppDispatch();\r\n\r\n const formData = {...}\r\n\r\n const onSubmitHandler = async (e: FormEvent, dispatch: any) => {\r\n e.preventDefault();\r\n dispatch(addComments(formData)); // 정상작동\r\n };\r\n\r\n return ( ...\r\n}\r\n\r\n```\r\n\r\n참고 : https://redux-toolkit.js.org/usage/usage-with-typescript\r\n"},{"title":"redux usage","description":"redux 사용해보기","category":"redux","date":"2023-03-04","content":"\r\n# React에서 Redux 사용해보기\r\n\r\n리덕스 툴킷 x\r\n\r\n## Redux 설치\r\n\r\n```bash\r\nyarn add redux react-redux\r\n```\r\n\r\n1. src 디렉토리 안에 redux 폴더 생성\r\n2. redux 폴더 안에 config, modules 폴더 생성 (생성할 state들의 그룹)\r\n3. modules 폴더 안 store.js 생성\r\n > 폴더/파일 명은 목적이나 폴더 구조에 맞춰 자유롭게 생성해줘도 괜찮다.\r\n\r\n## 스토어 생성\r\n\r\n```javascript\r\n// src/configStore.js\r\n\r\nimport { createStore, combineReducers } from \"redux\";\r\nimport todo from \"./modules/todo\"; // 모듈에 있는 reducer 불러오기\r\n\r\nconst rootReducer = combineReducers({});\r\nconst store = createStore(rootReducer);\r\n\r\nexport default store;\r\n```\r\n\r\n## 스토어 연동\r\n\r\n```javascript\r\n// 디렉토리 최상단 파일 ex/ index.js or app.js\r\n\r\nimport store from \"./redux/config/configStore\";\r\nimport { Provider } from \"react-redux\";\r\n\r\nconst root = ReactDOM.createRoot(document.getElementById(\"root\"));\r\nroot.render(\r\n //App을 Provider로 감싸주고, configStore에서 export default 한 store를 넣어준다.\r\n \r\n \r\n ,\r\n);\r\nreportWebVitals();\r\n```\r\n\r\n## reducer 편집\r\n\r\n```javascript\r\n// 예제\r\n// todo.js - ./modules에 있는 파일\r\n\r\n// Actions\r\nconst CREATE = \"my-app/todo/CREATE\";\r\nconst REMOVE = \"my-app/todo/REMOVE\";\r\n\r\n// Reducer\r\n//action이 없으면 state는 아래 initialState의 초기값, 있으면 action 값\r\nexport default function reducer(state = initialState, action = {}) {\r\n switch (action.type) {\r\n case CREATE: {\r\n const new_todo_list = [...state.todos, action.todo];\r\n return { todos: new_todo_list };\r\n }\r\n case REMOVE: {\r\n const new_todo_list = action.todo;\r\n return { todos: new_todo_list };\r\n }\r\n\r\n // ...etc\r\n\r\n default:\r\n return state;\r\n }\r\n}\r\n\r\n// Action Creators\r\nexport function createTodo(todo) {\r\n console.log(\"create 액션 생성\");\r\n return { type: CREATE, todo };\r\n}\r\n\r\nconst initialState = {\r\n // createTodo(todo)의 초기값 설정\r\n todos: [\r\n {\r\n id: 1,\r\n name: \"qwert\",\r\n },\r\n {\r\n id: 2,\r\n name: \"asdf\",\r\n },\r\n ],\r\n};\r\n\r\nexport function removeTodo(todo) {\r\n console.log(\"remove 액션 생성\");\r\n return { type: REMOVE, todo };\r\n}\r\n```\r\n\r\n## 컴포넌트에서의 사용\r\n\r\nreducer를 모두 작성해준 후 컴포넌트에서 사용할 수 있다.\r\n\r\n```javascript\r\n\r\n// 리덕스 훅\r\nimport {useSelector , useDispatch} from \"react-redux\";\r\n\r\n\r\nconst test = () =>{\r\n\t// 해당 상수는 useSelector를 통해 reducer에 있는 state 값을 가져온다.\r\n\tconst testList = useSelector((state=>state.todo.todos))\r\n // 액션을 생성하기 위해 준비\r\n const dispatch = useDispatch()\r\n\r\n const addBtn = () =>{\r\n \t// ---실행하고싶은 코드들---\r\n // -------------------------\r\n // dispatch로 액션을 생성했으니, 원하는 처리는 reducer에서 입력하면 된다.\r\n dispatch(createTodo(reducer에서 처리하고 싶은 값))\r\n }\r\n return(\r\n \t
\r\n )\r\n}\r\n\r\n```\r\n\r\n참조 : https://ko.redux.js.org/\r\n"},{"title":"react datepicker","description":"React datepicker 사용하기","category":"react","date":"2023-03-03","content":"\r\n# React Datepicker 사용하기\r\n\r\n공식 문서\r\n\r\nhttps://reactdatepicker.com/\r\n\r\n### package 설치\r\n\r\n```bash\r\nyarn add react-datepicker\r\n```\r\n\r\n패키지 설치 후 아래와 같이 import해 사용할 수 있다.\r\n\r\n```javascript\r\nimport DatePicker from \"react-datepicker\"; // 데이트픽커 import\r\nimport \"react-datepicker/dist/react-datepicker.css\"; // 데이트픽커 기본 CSS\r\nimport { ko } from \"date-fns/esm/locale\"; // 한국어 변환\r\n\r\nexport default function DatePickerTest() {\r\n const [startDate, setStartDate] = useState(new Date());\r\n const [endDate, setEndDate] = useState(null);\r\n\r\n return (\r\n <>\r\n \r\n \r\n );\r\n}\r\n```\r\n\r\n달력은 props 설정을 통해 다양한 방식의 달력을 설정해줄 수 있다.\r\n\r\n아래는 범위 지정 달력을 만드는 코드이다.\r\n\r\n```javascript\r\nexport default function DatePickerTest() {\r\n const [startDate, setStartDate] = useState(new Date());\r\n const [endDate, setEndDate] = useState(null);\r\n\r\n const onChange = dates => {\r\n const [start, end] = dates;\r\n setStartDate(start);\r\n setEndDate(end);\r\n };\r\n\r\n return (\r\n \r\n );\r\n}\r\n```\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5DkxI%2FbtrMUTz6YZb%2FpYNfcGfYjpM5GZlENLALTk%2Fimg.png)\r\n\r\n위 코드는 selectsRange라는 속성의 달력을 이용한다.\r\n다양한 속성은 상단의 공식 페이지 링크를 참고하면 좋다.\r\n\r\n## 스타일 적용\r\n\r\ncustom header 같은 props 옵션 등을 통해 css 스타일을 줄 수 있지만, 스타일이 원하는대로 입히기 어려운 점도 있었고 전체적인 코드가 보기 불편하다는 생각이 들어 styled component를 통해 css를 적용해줬다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzDzqF%2FbtrMWeKqb6T%2FkKiJfjfF1fP7vPcnWpyHOK%2Fimg.png)\r\n\r\n```javascript\r\n\r\nconst StyledDatePickerWrapper = styled.section`\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n width: 300px;\r\n height: 380px;\r\n border-radius: 20px;\r\n box-shadow: 0 0 5px 0 rgb(71, 181, 255);\r\n\r\n .react-datepicker {\r\n background-color: white;\r\n border-color: rgb(198, 232, 255);\r\n border-radius: 20px;\r\n }\r\n\r\n /* ... */\r\n\r\n```\r\n\r\n코드가 길어지더라도 jsx와 철저하게 분리하고 싶다면 이런 방법도 나쁘지 않은 것 같다.\r\n"},{"title":"react media query","description":"React mediaQuery 사용법","category":"react","date":"2023-03-03","content":"\r\n# react-responsive 사용해보기\r\n\r\n공식 사이트 : https://yarnpkg.com/package/react-responsive\r\n\r\nCSS의 mediaQuery를 사용해볼 수도 있지만, 간편하게 적용할 수 있는 패키지가 있어서 사용해봤다.\r\n\r\n### 패키지 설치\r\n\r\n```bash\r\nyarn add react-responsive\r\n```\r\n\r\n### useMediaQuery 세팅\r\n\r\n```javascript\r\n// useMideaQuery.js\r\n\r\nimport { useMediaQuery } from \"react-responsive\";\r\n\r\nexport const Desktop = ({ children }) => {\r\n const isDesktop = useMediaQuery({\r\n minWidth: 481,\r\n });\r\n return isDesktop ? children : null;\r\n};\r\n\r\nexport const Mobile = ({ children }) => {\r\n const isMobile = useMediaQuery({ maxWidth: 480 });\r\n return isMobile ? children : null;\r\n};\r\n```\r\n\r\n위의 경우 모바일과 PC만 나눴지만 대략적인 분기점은 아래와 같이 설정해도 괜찮다\r\n\r\n1. 낮은 해상도의 PC, 태블릿 가로 : ~1024px\r\n2. 태블릿 가로 : 768px ~ 1023px\r\n3. 모바일 가로, 태블릿 : 480px ~ 767px\r\n4. 모바일 : ~480\r\n\r\n### useMediaQuery 사용\r\n\r\n```javascript\r\n// Responsive.jsx\r\n\r\nimport { Desktop, Mobile } from \"../../hooks/useMideaQuery\";\r\n\r\nexport const Responsive = () => {\r\n return (\r\n <>\r\n \r\n
PC화면
\r\n
\r\n \r\n
모바일화면
\r\n
\r\n \r\n );\r\n};\r\n```\r\n\r\n편의상 한 컴포넌트에 다 집어넣을 수도 있지만, 모바일과 데스크탑 환경의 view는 아예 다른 경우가 많기 때문에, 코드 관리를 편하게 하기 위해서는 모바일 컴포넌트를 따로 만드는게 더 나을 것 같다는 생각이 든다.\r\n"},{"title":"react modal non library","description":"라이브러리 없이 React Modal 만들기","category":"react","date":"2023-03-03","content":"\r\n# 라이브러리 없이 모달창 만들어보기\r\n\r\n모달 버튼이 있는 페이지\r\n\r\n```javascript\r\n// ModalTest.jsx\r\n\r\nimport ModalPage from \"./ModalPage\";\r\nimport { useState } from \"react\";\r\n\r\nexport default function ModalTest() {\r\n // 모달을 보여줄지 말지 상태를 관리하는 state를 만들어준다.\r\n const [showModal, setShowModal] = useState(false);\r\n\r\n // 모달 버튼을 클릭하면 열리고\r\n const openModal = () => {\r\n setShowModal(true);\r\n };\r\n // 활성화된 모달창 밖을 클릭하면 닫힌다.\r\n const closeModal = () => {\r\n setShowModal(false);\r\n };\r\n\r\n return (\r\n <>\r\n \r\n // 모달상태가 true면 ModalPage를 보여주고, 아니면 null // props로 showModal과\r\n closeModal을 전달한다.\r\n {showModal === true ? (\r\n \r\n ) : null}\r\n \r\n );\r\n}\r\n```\r\n\r\n모달 컴포넌트\r\n\r\n```javascript\r\nimport styled from \"styled-components\";\r\n\r\nexport default function ModalPage({ showModal, closeModal }) {\r\n return (\r\n // 모달 밖을 클릭하면 모달창을 닫게 만든다.\r\n \r\n // stopPropagation은 부모태그로부터의 이벤트 전파를 중지시킨다. // 이\r\n 친구가 없으면 모달창 안쪽을 클릭해도 closeModal이 실행된다.\r\n e.stopPropagation()}>\r\n 열린 모달창이에옹\r\n \r\n \r\n );\r\n}\r\n\r\n// 위치를 대략 가운대로 정해주고\r\nconst StyledModalContainer = styled.div`\r\n position: fixed;\r\n left: 50%;\r\n top: 50%;\r\n transform: translate(-50%, -50%);\r\n`;\r\n\r\n// 모달 밖 배경색은 우리에게 익숙한 어두운 색으로 만들어주자\r\nconst StyledModalBackground = styled.div`\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n bottom: 0;\r\n right: 0;\r\n background-color: rgba(0, 0, 0, 0.4);\r\n z-index: 0;\r\n cursor: auto;\r\n`;\r\n\r\n// 모달창 사이즈는 용도에 맞춰 설정해준다.\r\nconst StyledModal = styled.div`\r\n width: 400px;\r\n height: 400px;\r\n background-color: white;\r\n`;\r\n```\r\n\r\n브라우저 화면\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOLJGf%2FbtrNjUdVndT%2FvgVskBO2xWeRwi01YQPzkk%2Fimg.png)\r\n\r\n스타일은 용도에 맞게 다양하게 변경시킬 수 있다.\r\n"},{"title":"react navigate props","description":"React navigate로 props 넘기기","category":"react","date":"2023-03-03","content":"\r\n# useNavigate를 이용한 props 전달\r\n\r\n### useNavigate로 다른 페이지의 화면으로 이동할 때, props처럼 값을 넘기는 방법을 사용해볼 수 있다.\r\n\r\n```javascript\r\n\r\n// 보내는컴포넌트.jsx\r\n\r\nimport { useNavigate } from \"react-router-dom\";\r\n\r\nconst Component1 = () => {\r\nconst navigate = useNavigate()\r\n\r\nlet someDatas = {\r\n\tname:\"hihi\"\r\n value:\"here\"\r\n}\r\n\r\nreturn\r\n
navigate(`/다른페이지주소`,{ state: someDatas });}>\r\n\t다른페이지로 이동\r\n
\r\n}\r\n\r\n\r\n\r\n// 받는 컴포넌트.jsx\r\n\r\nimport { useLocation } from \"react-router-dom\";\r\n\r\nconst Component2 = () => {\r\n\tconst location = useLocation();\r\n\tconsole.log(location);\r\n}\r\n\r\n```\r\n\r\n위와 같이 작성은 아래와 같이 데이터를 받아오는 것을 보여준다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedpbW9%2FbtrRGwsRGuz%2FKp5GIecYeABXw1FPbGLdsK%2Fimg.png)\r\n\r\n하지만 데이터를 받는 페이지가 직접적으로 접속할 수 있는 페이지라면 그렇게 접속한 페이지는 아무 데이터가 없는 화면으로 노출될 수 있기 때문에, 전역 상태를 관리해줄지, 데이터를 넘겨줄지 상황에 알맞는 방식을 적용하는 것이 중요하다.\r\n"},{"title":"react simple formdata code","description":"react formdata 코드간소화","category":"react","date":"2023-03-03","content":"\r\n# FormData 저장 코드 간소화하기\r\n\r\n하나의 폼에서 여러 개의 input 값을 변경하기 위해 함수를 만들어 간단하게 코드를 간소화시킬 수 있다.\r\n\r\n```typescript\r\n// form.tsx\r\n onSubmitHandler(e, dispatch)}>\r\n onChangeHandler(e)}\r\n />\r\n
\r\n onChangeHandler(e)}\r\n required\r\n />\r\n
\r\n onChangeHandler(e)}\r\n required\r\n >\r\n
\r\n \r\n
\r\n \r\n
;\r\n\r\n// useForm.tsx\r\nconst [formData, setFormData] = useState({\r\n profile_url: \"\",\r\n author: \"\",\r\n content: \"\",\r\n createdAt: todayDate(),\r\n});\r\n\r\nconst onChangeHandler = (e: InputEvent) => {\r\n let type = e.target.name;\r\n let value = e.target.value;\r\n if (type === \"profile_url\")\r\n setFormData(prev => ({ ...prev, profile_url: value }));\r\n if (type === \"author\") setFormData(prev => ({ ...prev, author: value }));\r\n if (type === \"content\") setFormData(prev => ({ ...prev, content: value }));\r\n setFormData(prev => ({ ...prev, [type]: value }));\r\n};\r\n```\r\n\r\n하지만 위와 같이 if문이 많아져 코드의 성능과 가독성을 떨어뜨릴 수 있기 때문에, 아래와 같이 변경해볼 수 있다.\r\n\r\n```typescript\r\n// useForm.tsx\r\n\r\nconst [formData, setFormData] = useState({\r\n profile_url: \"\",\r\n author: \"\",\r\n content: \"\",\r\n createdAt: todayDate(),\r\n});\r\n\r\nconst onChangeHandler = (e: InputEvent) => {\r\n const { name, value } = e.target.name;\r\n setFormData(prev => ({ ...prev, [name]: value }));\r\n};\r\n```\r\n\r\nhtml input의 name 속성을 이용해 setState 코드를 간소화시킬 수 있는 방법이다.\r\n"},{"title":"react styled components hover","description":"react에 styled-components를 이용해 hover 적용하기","category":"react","date":"2023-03-03","content":"\r\n# 리액트 스타일 컴포넌트에 hover 적용하기\r\n\r\n```typescript\r\n// &:hover를 넣는다.\r\n\r\nconst ButtonStyle = styled.button`\r\n &:hover {\r\n background-color: skyblue;\r\n color: blue;\r\n }\r\n`;\r\n```\r\n\r\n비슷한 상황에서 a 태그를 사용할때 자동으로 설정되는 스타일도 수정해줄 수 있다.\r\n\r\n```typescript\r\nconst LinkTag = styled.a`\r\n /* 밑줄 제거 */\r\n text-decoration: none;\r\n\r\n /* 마우스를 링크에 올려뒀을 때의 스타일 */\r\n &:hover {\r\n }\r\n /* 아직 방문하지 않은 링크의 스타일 */\r\n &:link {\r\n }\r\n /* 사용자가 방문한 적이 있는(클릭한 이후) 링크의 스타일 */\r\n &:visited {\r\n }\r\n /* 마우스로 링크를 클릭하고 뗄 때까지의 스타일 */\r\n &:active {\r\n }\r\n`;\r\n```\r\n"},{"title":"react submit prevent default","description":"form 태그에서 submit 이벤트 방지하기","category":"react","date":"2023-03-03","content":"\r\n# react form에서 submit 이벤트 방지하기\r\n\r\n### submit 이벤트가 발생하면 페이지가 새로고침이 된다.\r\n\r\n단순히 페이지가 리프레시 되는 것도 좋은 사용자 경험이 아닌데, 임시로 저장돼있던 데이터(회원가입 정보 등)가 날아가면 React의 작동 방식을 거스를 뿐 아니라 사용자 경험에도 치명적이다.\r\n\r\n떄문에 우리는 sumbit 이벤트를 멈춰줄 필요가 있다.\r\n\r\n```javascript\r\nconst Header = () => {\r\n const onSubmit = e => {\r\n e.preventDefault(); // 해당 코드로 이벤트를 멈춰줄 수 있다.\r\n };\r\n\r\n return (\r\n
\r\n \r\n // 버튼 타입도 submit으로 꼭 변경해준다.\r\n \r\n
\r\n );\r\n};\r\n```\r\n\r\n위와 같이 작성하면 같은 폼 안에 있는 경우에 대해 submit 방지가 잘 되는 것을 확인할 수 있다.\r\n"},{"title":"react z index error","description":"z-index가 올바르게 적용되지 않을때 적용해볼 수 있는 방법","category":"react","date":"2023-03-03","content":"\r\n# z-index 미적용시 해결방법\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2c0QV%2FbtrXD8G7Ar9%2FpCFQ6eujsGgA0bKCKx1kpk%2Fimg.png)\r\n\r\n위와 같이 z-index를 999로 적용해도 원하는대로 작동하지 않는 경우를 볼 수 있다.\r\n\r\n### MDN 공식 문서 : https://developer.mozilla.org/ko/docs/Web/CSS/z-index\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8pPBj%2FbtrXCijsq0h%2FkdCdCRUKvZIGe7dk8KPJf0%2Fimg.png)\r\n\r\n해당 사진의 마지막 줄의 \"자손의 z-index를 자기 외의 바깥 요소와 비교하지 않습니다.\" 이 부분으로 인한 문제로 생각됐다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fct1Z18%2FbtrXH8zek29%2Fjwx0ckk9YgnPRLS2sM06k0%2Fimg.png)\r\n\r\n부모 요소는 부모 요소끼리, 자식 요소는 자식 요소끼리 경쟁하기 때문에, 기존 코드는 이와 같이 HeaderNav 안에 SideMenu가 있어 HeaderNav 내에서만 z-index를 비교하고 있던 것이었다.\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUIap4%2FbtrXCicOSRi%2FzWiWf6wrtlUx9aK2uYKK7K%2Fimg.png)\r\n\r\n이처럼 SideMenu를 HeaderNav 밖으로 빼주니, 아래와 같이 정상적으로 작동하는 모습을 볼 수 있었다.\r\n\r\n### 정상 적용된 화면\r\n\r\n![image](https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwMc3x%2FbtrXFupLVFz%2FWH6kLZ1IbRxHnXK2bqkHa0%2Fimg.png)\r\n\r\n## z-index가 제대로 작동하지 않을때 참고할만한 사항\r\n\r\n1. 부모 요소는 부모 요소끼리 경쟁되고 있는지 확인한다.\r\n2. Element가 static(position 속성의 default 값)이 아닌 position 속성이 설정되어있는지 확인한다.(relative, absolute, fixed, sticky)\r\n3. opacity나 transform과 같은 css 속성이 설정돼있는지 확인한다. 해당 css요소가 설정돼있다면 1번의 기준에 맞춰 코드를 다시 작성해준다.\r\n\r\n이정도면 버그를 수정하는데 충분하지만 더 싶도 깊은 내용은 아래를 참고해보면 좋을 것 같다.\r\n\r\n참조 : https://coder-coder.com/z-index-isnt-working/\r\n"},{"title":"typescript syntax","description":"Typescript 사용을 위한 기본적인 문법 정리","category":"typescript","date":"2023-03-02","content":"\r\n# 기본 TypeScript 타입 선언\r\n\r\n```typescript\r\n\r\n// 문자열\r\nlet str: string = \"hello\";\r\n\r\n// 숫자\r\nlet num: number = 100;\r\n\r\n// 배열\r\nlet arr: Array = [10,20,30];\r\nlet arr2: number[] = [10,20,30];\r\nlet arr3: Array = [\"hello\",\"hellololo\" ];\r\nlet arr4: [string, number] = [\"hello\", 182];\r\n\r\n// 객체\r\nlet obj:object = {name:\"hello\", age:29};\r\nlet person:{name: string, age:number};\r\n\r\n// Boolean\r\nlet isAvaliable: boolean = true;\r\n\r\n\r\n// 함수 선언\r\nparameter와 return 값에 대해 타입 선언 가능\r\nconst sum = (a:number, b:number):number => {\r\nreturn a+b;\r\n}\r\n\r\n// optional parameter일 경우 ?를 사용\r\n\r\nconst log = (time: string, result?: string, option?: string) => {\r\nconsole.log(time, result, option);\r\n}\r\nlog(\"2021-10-04\", \"success\");\r\n\r\n```\r\n\r\n# 인터페이스 (interface)\r\n\r\n### 자주 사용하는 타입들을 object 형태의 묶음으로 정의해 새로운 타입을 만들어 사용하는 기능\r\n\r\n```typescript\r\n\r\n// interface 선언\r\ninterface User {\r\nage: number;\r\nname: string;\r\n}\r\n\r\n// 변수 활용\r\nconst hanbbi: User = { age: 30, name: \"hello\"}\r\n\r\n\r\n// 함수 인자로의 활용\r\nconst getUser = (user:User){\r\nconsole.log(user);\r\n}\r\ngetUser({ age:10, name: \"hanbbi\" })\r\n\r\n\r\n\r\n// 함수 구조 활용\r\ninterface Sum {\r\n(a:number, b:number): number;\r\n}\r\n\r\nlet sumFinc: Sum:\r\nsumFunc = function(a: number, b: number): number {\r\nreturn a+b;\r\n}\r\n\r\n\r\n\r\n// 배열 활용\r\ninterface StringArray {\r\n[index:number]: string;\r\n}\r\n\r\nlet arr: StringArray = [\"a\", \"b\", \"c\"];\r\n\r\n\r\n// 객체 활용\r\ninterface StringRegexObject {\r\n[key: string]: RegExp;\r\n}\r\n\r\nconst obj: StringRegexObject {\r\ncssFile: /\\.css$/,\r\n jsFile: /\\.js$/\r\n}\r\n\r\n\r\n// interface 확장 (extends 사용)\r\ninterface Person {\r\nname: string;\r\nage:number;\r\n}\r\ninterface Developer extends Person {\r\nskill: string;\r\n}\r\n\r\nconst juniorDeveloper = {\r\nname: \"hanbbi\",\r\nage:100,\r\nskill: \"JS\"\r\n}\r\n\r\n```\r\n\r\n# 타입 별칭(type aliases)\r\n\r\n### 타입 키워드는 interface와 다르게 새로운 타입을 생성하는 것이 아닌 별칭을 부여하는 것으로, extends 키워드는 사용할 수 없음\r\n\r\n```typescript\r\n\r\n// 타입 별칭 선언 및 활용\r\ntype MyString = string;\r\nconst str: MyString = \"Hello dear\"\r\n\r\ntype Todo = {\r\nid: string;\r\ntitle: string;\r\ndone: boolean\r\n}\r\n\r\nconst getTodo(todo:Todo){\r\nconsole.log(todo);\r\n}\r\n\r\n```\r\n\r\n# 연산자 (Operator)\r\n\r\n## Uinon Type\r\n\r\n### 한 가지 이상의 type을 선언하고자 할 때 사용 가능. | 기호 사용\r\n\r\n```typescript\r\nconst logMessage = (value: string | number) => {\r\n if (typeof value === \"string\") {\r\n value.toString();\r\n } else if (typeof value === \"number\") {\r\n value.toLocaleString();\r\n } else {\r\n throw new TypeError(\"value must be string or number\");\r\n }\r\n};\r\nlogMessage(\"hello\");\r\nlogMessage(1000);\r\n```\r\n\r\n## intersection Type\r\n\r\n### 합집합과 같은 개념으로, 함수 호출의 경우 함수 인자에 명시한 type을 모두 제공해야 한다. & 기호 사용\r\n\r\n```typescript\r\n\r\ninterface Zoo {\r\nname: string;\r\nlocation: string\r\nprice: number;\r\n}\r\n\r\ninterface Animal {\r\nname: string;\r\ncount: number;\r\n}\r\n\r\nconst askZookeeper = ( value : Zoo & Animal ) => {\r\n // value 는\r\n { name:\"어린이대공원\", location: \"서울시 광진구\", price: 10000, count: 10000}\r\n // 와 같이 Zoo와 Animal이 모두 포함되는 인자를 줘야한다.\r\n}\r\n\r\n```\r\n\r\n# Enum\r\n\r\n### enum 키워드를 사용하면 일종의 default 값을 선언할 수 있다.\r\n\r\n```typescript\r\n\r\n// 숫자형 enum\r\n// 자동으로 0에서 1씩 증가하는 값을 부여\r\n\r\nenum Shoes {\r\nNike, // 0\r\nAdidas, // 1\r\nNewBalance //2\r\n}\r\nconst myShoes = Shoes.Nike; // 0\r\n\r\n문자형 enum\r\nenum Food {\r\ncake = \"케익\",\r\ncookie = \"쿠키\"\r\n}\r\nconst player = Food.cookie; // 쿠키\r\n\r\n```\r\n\r\n# 제네릭\r\n\r\n### 제네릭을 활용하면 인자를 넘겨 호출하는 시점에 타입을 결정할 수 있다. 제네릭 활용 시 동일한 기능을 하는 함수를 일일이 만들 필요가 없으며, 타입 추론에 있어 강점을 가진다.\r\n\r\n제네릭 선언\r\n와 같이 타입을 선언한다. 알파벳은 통상 T로 정해져 있다.\r\n\r\n```typescript\r\n\r\nconst logText = (text: T):T => {\r\nconsole.log(text);\r\nreturn text;\r\n}\r\nlogText(\"Hello hanbbi\");\r\n\r\n\r\n// interface에 제네릭 선언\r\n\r\ninterface Dropdown {\r\nvalue: T;\r\nselected: boolean;\r\n}\r\ncosnt obj: Dropdown = { value: \"hamburger\" , selected: true};\r\n\r\n```\r\n\r\n# 제네릭 타입 제한\r\n\r\n## 1. 배열 힌트\r\n\r\n```typescript\r\n\r\nconst logTextLength = (text: T[]): T[] =>{\r\nconsole.log(text.length);\r\ntext.forEach(text =>{\r\nconsole.log(text):\r\n});\r\n}\r\nlogTextLength([\"hi\", \"hello\"]);\r\n\r\n\r\n```\r\n\r\n## 2. 정의된 타입 이용(extends)과 keyof\r\n\r\n```typescript\r\n\r\ninterface ShoppingItem {\r\nname: string;\r\nprice: number;\r\nstock: number;\r\n}\r\n\r\nconst getShoppingItemOption(itemOption: T): T {\r\nreturn itemOption;\r\n}\r\n\r\n// \"name\", \"price\", \"stock\"만 인자로 가능\r\ngetShoppingItemOption(\"price\");\r\n\r\n```\r\n\r\n# 타입 추론 (Type inference)\r\n\r\n## 1. 기본 변수 타입 추론\r\n\r\n```typescript\r\n\r\n// string으로 추론\r\nlet a = \"abc\";\r\n\r\n// a: number로 추론\r\n// b: string으로 추론\r\n// return value는 string으로 추론\r\nconst getValue(a = 10) {\r\nlet b = \"hello\";\r\nreturn a + b;\r\n}\r\n\r\n\r\n```\r\n\r\n## 2. interface추론\r\n\r\n```typescript\r\n\r\n// interface 1개\r\ninterface Dropdown {\r\nvalue: T;\r\ntitle: string;\r\n}\r\nconst shoppingItem:Dropdown ={\r\nvalue: 10000;\r\ntitle: \"shoe\"\r\n}\r\n// interface 2개\r\ninterface Dropdown2 {\r\nvalue: T;\r\ntitle: string;\r\n}\r\ninterface DetailedDropdown extends Dropdown2{\r\ntag: K;\r\ndesc: string;\r\n}\r\nconst detailed: DetailedDropdown{\r\nvalue: \"10000\";\r\ntitle: \"shoe\",\r\ntag: \"10000\",\r\ndesc: \"description\"\r\n}\r\n\r\n```\r\n\r\n# 타입 단언 (Type assertion)\r\n\r\n### as 키워드를 사용해 타입을 정함으로써 typescript에게 타입을 알려줄 수 있다. 주로 DOM API를 조작할 떄 사용한다.\r\n\r\n```typescript\r\n// div가 있는지 장담할 수 없음, HTMLDivElement | null\r\n// 따라서 typescript에게 타입을 단언해 타입을 알려줄 수 있음.\r\nconst div = document.querySelector(\"div\") as HTMLDivElement;\r\ndiv.innerText = \"test\";\r\n```\r\n\r\n# 타입 가드 (Type guard)\r\n\r\n### union type을 사용하는 경우 공통된 속성만 접근 가능하며, 로직상 공통되지 않은 속성에 접근하고자 할 때 불편함을 해소하기 위해 타입 단언으로 공통되지 않은 속성에 접근하고자 하는 방법이 있지만, 코드 가독성을 위해 타입 가드 방법을 주로 사용한다.\r\n\r\n```typescript\r\nconst isDeveloper = (target: Developer | Humanoid): target is Developer => {\r\n return (target as Developer).skill !== undefined;\r\n};\r\nif (isDeveloper(tom)) {\r\n console.log(tom.name);\r\n console.log(tom.skill);\r\n} else {\r\n console.log(tom.name);\r\n console.log(tom.age);\r\n}\r\n```\r\n\r\n# 타입 호환 (Type compatibility)\r\n\r\n### TypeScript에서 더 큰 타입 구조를 갖는 변수에 작은 타입 구조를 갖는 변수를 할당 가능\r\n\r\n```typescript\r\nlet add = function (a: number) {\r\n // ...\r\n};\r\nlet sum = function (a: number, b: number) {\r\n // ...\r\n};\r\n// 아래는 에러가\r\n// add = sum;\r\n\r\n// 에러가 나지 않는 방식. sum의 구조가 더 크다고 볼 수 있음\r\nsum = add;\r\n```\r\n\r\n참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/\r\n"},{"title":"html input basic","description":"HTML input 속성 이용하기","category":"html","date":"2023-02-09","content":"\r\n# Input element 사용하기\r\n\r\n## 타입\r\n\r\n- type=\"text\" 일반 텍스트\r\n- type=\"password\" 패스워드 형식(마스킹 처리)\r\n- type=\"eamil\" 이메일 형식\r\n- type=\"radio\" 설문 등 여러 선택지 중 하나의 값을 선택하는 형식\r\n- type=\"checkbox\" 설문 등 여러 선택지 중 여러 값을 선택하는 형식\r\n- type=\"number\" 숫자\r\n- type=\"file\" 파일 업로드\r\n- type=\"date\" 달력\r\n- type=\"range\" 볼륨 조절 위젯같이 값이 가려진 숫자를 입력하는 조작 가능\r\n\r\n## 속성\r\n\r\n- accept=\"업로드할 파일의 기댓값 암시\"\r\n- placeholder=\"input 안 힌트 텍스트\"\r\n- autofocus \"페이지 전환 시 자동으로 맨 처음 포커싱\"\r\n- required \"필수값\"\r\n- maxlength=\"최댓값\"\r\n- disabled \"비활성화\"\r\n- autocomplete=\"off\" \"브라우저의 자동완성 제거\"\r\n- checked \"radio나 checkbox 등의 체크 여부\"\r\n- name input의 이름으로 radio나 checkbox의 카테고리 묶음이나 javascript 이벤트 등을 유연하게 적용할 수 있음\r\n"}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-1-num-str.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-1-num-str.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-1-num-str.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-1-num-str.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-2-date.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-2-date.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-2-date.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-2-date.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-3-math.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-3-math.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-3-math.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-3-math.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-4-array-1.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-4-array-1.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-4-array-1.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-4-array-1.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-4-array-2.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-4-array-2.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-4-array-2.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-4-array-2.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-5-loop.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-5-loop.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/javascript-basic-5-loop.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-basic-5-loop.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/javascript-dynamic-import.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-dynamic-import.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/javascript-dynamic-import.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-dynamic-import.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/javascript-how-to-use-promise.all.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-how-to-use-promise.all.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/javascript-how-to-use-promise.all.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-how-to-use-promise.all.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/javascript-jest-test-code.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-jest-test-code.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/javascript-jest-test-code.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript-jest-test-code.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/javascript.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/javascript.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/javascript.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nestjs-custom-repository.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs-custom-repository.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nestjs-custom-repository.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs-custom-repository.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nestjs-localhost-https.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs-localhost-https.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nestjs-localhost-https.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs-localhost-https.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nestjs-server-client-cookie.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs-server-client-cookie.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nestjs-server-client-cookie.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs-server-client-cookie.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nestjs-server-client-cookie2.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs-server-client-cookie2.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nestjs-server-client-cookie2.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs-server-client-cookie2.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nestjs-use-bcrypt.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs-use-bcrypt.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nestjs-use-bcrypt.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs-use-bcrypt.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nestjs.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nestjs.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nestjs.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-link-userouter.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-link-userouter.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-link-userouter.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-link-userouter.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-loading.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-loading.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-loading.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-loading.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-marked-webpack-imported-module-7-default.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-marked-webpack-imported-module-7-default.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-marked-webpack-imported-module-7-default.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-marked-webpack-imported-module-7-default.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-pages-api-dir-unsupported.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-pages-api-dir-unsupported.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-pages-api-dir-unsupported.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-pages-api-dir-unsupported.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-prop-classname-did-not-match.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-prop-classname-did-not-match.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-prop-classname-did-not-match.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-prop-classname-did-not-match.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-react-responsive.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-react-responsive.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nextjs-react-responsive.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs-react-responsive.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/nextjs.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/nextjs.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/nextjs.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-common-components.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-common-components.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-common-components.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-common-components.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-component-lifecycle.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-component-lifecycle.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-component-lifecycle.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-component-lifecycle.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-context-api.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-context-api.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-context-api.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-context-api.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-datepicker.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-datepicker.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-datepicker.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-datepicker.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-media-query.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-media-query.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-media-query.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-media-query.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-memo.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-memo.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-memo.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-memo.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-modal-non-library.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-modal-non-library.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-modal-non-library.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-modal-non-library.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-navigate-props.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-navigate-props.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-navigate-props.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-navigate-props.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-simple-formdata-code.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-simple-formdata-code.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-simple-formdata-code.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-simple-formdata-code.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-smooth-scroll.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-smooth-scroll.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-smooth-scroll.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-smooth-scroll.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-styled-components-hover.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-styled-components-hover.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-styled-components-hover.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-styled-components-hover.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-submit-prevent-default.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-submit-prevent-default.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-submit-prevent-default.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-submit-prevent-default.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-type-assertion.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-type-assertion.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-type-assertion.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-type-assertion.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-usecallback.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-usecallback.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-usecallback.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-usecallback.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-usememo.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-usememo.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-usememo.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-usememo.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react-z-index-error.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-z-index-error.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react-z-index-error.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react-z-index-error.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/react.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/react.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/react.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/redux-mock-server.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/redux-mock-server.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/redux-mock-server.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/redux-mock-server.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/redux-toolkit-async-thunk.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/redux-toolkit-async-thunk.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/redux-toolkit-async-thunk.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/redux-toolkit-async-thunk.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/redux-typescript-react-reduxtoolkit.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/redux-typescript-react-reduxtoolkit.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/redux-typescript-react-reduxtoolkit.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/redux-typescript-react-reduxtoolkit.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/redux-usage.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/redux-usage.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/redux-usage.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/redux-usage.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/redux.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/redux.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/redux.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/redux.json diff --git a/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/sw-test.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/sw-test.json new file mode 100644 index 00000000..bec2cf6b --- /dev/null +++ b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/sw-test.json @@ -0,0 +1 @@ +{"pageProps":{"title":"sw test","description":"정보처리기사 테스트","category":"정보처리기사","date":"2023-12-27","content":"\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n```typescript\r\n\r\n// 문자열\r\nlet str: string = \"hello\";\r\n\r\n// 숫자\r\nlet num: number = 100;\r\n\r\n// 배열\r\nlet arr: Array = [10,20,30];\r\nlet arr2: number[] = [10,20,30];\r\nlet arr3: Array = [\"hello\",\"hellololo\" ];\r\nlet arr4: [string, number] = [\"hello\", 182];\r\n\r\n// 객체\r\nlet obj:object = {name:\"hello\", age:29};\r\nlet person:{name: string, age:number};\r\n\r\n// Boolean\r\nlet isAvaliable: boolean = true;\r\n\r\n\r\n// 함수 선언\r\nparameter와 return 값에 대해 타입 선언 가능\r\nconst sum = (a:number, b:number):number => {\r\nreturn a+b;\r\n}\r\n\r\n// optional parameter일 경우 ?를 사용\r\n\r\nconst log = (time: string, result?: string, option?: string) => {\r\nconsole.log(time, result, option);\r\n}\r\nlog(\"2021-10-04\", \"success\");\r\n\r\n```\r\n\r\n# 인터페이스 (interface)\r\n\r\n### 자주 사용하는 타입들을 object 형태의 묶음으로 정의해 새로운 타입을 만들어 사용하는 기능\r\n\r\n```typescript\r\n\r\n// interface 선언\r\ninterface User {\r\nage: number;\r\nname: string;\r\n}\r\n\r\n// 변수 활용\r\nconst hanbbi: User = { age: 30, name: \"hello\"}\r\n\r\n\r\n// 함수 인자로의 활용\r\nconst getUser = (user:User){\r\nconsole.log(user);\r\n}\r\ngetUser({ age:10, name: \"hanbbi\" })\r\n\r\n\r\n\r\n// 함수 구조 활용\r\ninterface Sum {\r\n(a:number, b:number): number;\r\n}\r\n\r\nlet sumFinc: Sum:\r\nsumFunc = function(a: number, b: number): number {\r\nreturn a+b;\r\n}\r\n\r\n\r\n\r\n// 배열 활용\r\ninterface StringArray {\r\n[index:number]: string;\r\n}\r\n\r\nlet arr: StringArray = [\"a\", \"b\", \"c\"];\r\n\r\n\r\n// 객체 활용\r\ninterface StringRegexObject {\r\n[key: string]: RegExp;\r\n}\r\n\r\nconst obj: StringRegexObject {\r\ncssFile: /\\.css$/,\r\n jsFile: /\\.js$/\r\n}\r\n\r\n\r\n// interface 확장 (extends 사용)\r\ninterface Person {\r\nname: string;\r\nage:number;\r\n}\r\ninterface Developer extends Person {\r\nskill: string;\r\n}\r\n\r\nconst juniorDeveloper = {\r\nname: \"hanbbi\",\r\nage:100,\r\nskill: \"JS\"\r\n}\r\n\r\n```\r\n\r\n# 타입 별칭(type aliases)\r\n\r\n### 타입 키워드는 interface와 다르게 새로운 타입을 생성하는 것이 아닌 별칭을 부여하는 것으로, extends 키워드는 사용할 수 없음\r\n\r\n```typescript\r\n\r\n// 타입 별칭 선언 및 활용\r\ntype MyString = string;\r\nconst str: MyString = \"Hello dear\"\r\n\r\ntype Todo = {\r\nid: string;\r\ntitle: string;\r\ndone: boolean\r\n}\r\n\r\nconst getTodo(todo:Todo){\r\nconsole.log(todo);\r\n}\r\n\r\n```\r\n\r\n# 연산자 (Operator)\r\n\r\n## Uinon Type\r\n\r\n### 한 가지 이상의 type을 선언하고자 할 때 사용 가능. | 기호 사용\r\n\r\n```typescript\r\nconst logMessage = (value: string | number) => {\r\n if (typeof value === \"string\") {\r\n value.toString();\r\n } else if (typeof value === \"number\") {\r\n value.toLocaleString();\r\n } else {\r\n throw new TypeError(\"value must be string or number\");\r\n }\r\n};\r\nlogMessage(\"hello\");\r\nlogMessage(1000);\r\n```\r\n\r\n## intersection Type\r\n\r\n### 합집합과 같은 개념으로, 함수 호출의 경우 함수 인자에 명시한 type을 모두 제공해야 한다. & 기호 사용\r\n\r\n```typescript\r\n\r\ninterface Zoo {\r\nname: string;\r\nlocation: string\r\nprice: number;\r\n}\r\n\r\ninterface Animal {\r\nname: string;\r\ncount: number;\r\n}\r\n\r\nconst askZookeeper = ( value : Zoo & Animal ) => {\r\n // value 는\r\n { name:\"어린이대공원\", location: \"서울시 광진구\", price: 10000, count: 10000}\r\n // 와 같이 Zoo와 Animal이 모두 포함되는 인자를 줘야한다.\r\n}\r\n\r\n```\r\n\r\n# Enum\r\n\r\n### enum 키워드를 사용하면 일종의 default 값을 선언할 수 있다.\r\n\r\n```typescript\r\n\r\n// 숫자형 enum\r\n// 자동으로 0에서 1씩 증가하는 값을 부여\r\n\r\nenum Shoes {\r\nNike, // 0\r\nAdidas, // 1\r\nNewBalance //2\r\n}\r\nconst myShoes = Shoes.Nike; // 0\r\n\r\n문자형 enum\r\nenum Food {\r\ncake = \"케익\",\r\ncookie = \"쿠키\"\r\n}\r\nconst player = Food.cookie; // 쿠키\r\n\r\n```\r\n\r\n# 제네릭\r\n\r\n### 제네릭을 활용하면 인자를 넘겨 호출하는 시점에 타입을 결정할 수 있다. 제네릭 활용 시 동일한 기능을 하는 함수를 일일이 만들 필요가 없으며, 타입 추론에 있어 강점을 가진다.\r\n\r\n제네릭 선언\r\n와 같이 타입을 선언한다. 알파벳은 통상 T로 정해져 있다.\r\n\r\n```typescript\r\n\r\nconst logText = (text: T):T => {\r\nconsole.log(text);\r\nreturn text;\r\n}\r\nlogText(\"Hello hanbbi\");\r\n\r\n\r\n// interface에 제네릭 선언\r\n\r\ninterface Dropdown {\r\nvalue: T;\r\nselected: boolean;\r\n}\r\ncosnt obj: Dropdown = { value: \"hamburger\" , selected: true};\r\n\r\n```\r\n\r\n# 제네릭 타입 제한\r\n\r\n## 1. 배열 힌트\r\n\r\n```typescript\r\n\r\nconst logTextLength = (text: T[]): T[] =>{\r\nconsole.log(text.length);\r\ntext.forEach(text =>{\r\nconsole.log(text):\r\n});\r\n}\r\nlogTextLength([\"hi\", \"hello\"]);\r\n\r\n\r\n```\r\n\r\n## 2. 정의된 타입 이용(extends)과 keyof\r\n\r\n```typescript\r\n\r\ninterface ShoppingItem {\r\nname: string;\r\nprice: number;\r\nstock: number;\r\n}\r\n\r\nconst getShoppingItemOption(itemOption: T): T {\r\nreturn itemOption;\r\n}\r\n\r\n// \"name\", \"price\", \"stock\"만 인자로 가능\r\ngetShoppingItemOption(\"price\");\r\n\r\n```\r\n\r\n# 타입 추론 (Type inference)\r\n\r\n## 1. 기본 변수 타입 추론\r\n\r\n```typescript\r\n\r\n// string으로 추론\r\nlet a = \"abc\";\r\n\r\n// a: number로 추론\r\n// b: string으로 추론\r\n// return value는 string으로 추론\r\nconst getValue(a = 10) {\r\nlet b = \"hello\";\r\nreturn a + b;\r\n}\r\n\r\n\r\n```\r\n\r\n## 2. interface추론\r\n\r\n```typescript\r\n\r\n// interface 1개\r\ninterface Dropdown {\r\nvalue: T;\r\ntitle: string;\r\n}\r\nconst shoppingItem:Dropdown ={\r\nvalue: 10000;\r\ntitle: \"shoe\"\r\n}\r\n// interface 2개\r\ninterface Dropdown2 {\r\nvalue: T;\r\ntitle: string;\r\n}\r\ninterface DetailedDropdown extends Dropdown2{\r\ntag: K;\r\ndesc: string;\r\n}\r\nconst detailed: DetailedDropdown{\r\nvalue: \"10000\";\r\ntitle: \"shoe\",\r\ntag: \"10000\",\r\ndesc: \"description\"\r\n}\r\n\r\n```\r\n\r\n# 타입 단언 (Type assertion)\r\n\r\n### as 키워드를 사용해 타입을 정함으로써 typescript에게 타입을 알려줄 수 있다. 주로 DOM API를 조작할 떄 사용한다.\r\n\r\n```typescript\r\n// div가 있는지 장담할 수 없음, HTMLDivElement | null\r\n// 따라서 typescript에게 타입을 단언해 타입을 알려줄 수 있음.\r\nconst div = document.querySelector(\"div\") as HTMLDivElement;\r\ndiv.innerText = \"test\";\r\n```\r\n\r\n# 타입 가드 (Type guard)\r\n\r\n### union type을 사용하는 경우 공통된 속성만 접근 가능하며, 로직상 공통되지 않은 속성에 접근하고자 할 때 불편함을 해소하기 위해 타입 단언으로 공통되지 않은 속성에 접근하고자 하는 방법이 있지만, 코드 가독성을 위해 타입 가드 방법을 주로 사용한다.\r\n\r\n```typescript\r\nconst isDeveloper = (target: Developer | Humanoid): target is Developer => {\r\n return (target as Developer).skill !== undefined;\r\n};\r\nif (isDeveloper(tom)) {\r\n console.log(tom.name);\r\n console.log(tom.skill);\r\n} else {\r\n console.log(tom.name);\r\n console.log(tom.age);\r\n}\r\n```\r\n\r\n# 타입 호환 (Type compatibility)\r\n\r\n### TypeScript에서 더 큰 타입 구조를 갖는 변수에 작은 타입 구조를 갖는 변수를 할당 가능\r\n\r\n```typescript\r\nlet add = function (a: number) {\r\n // ...\r\n};\r\nlet sum = function (a: number, b: number) {\r\n // ...\r\n};\r\n// 아래는 에러가\r\n// add = sum;\r\n\r\n// 에러가 나지 않는 방식. sum의 구조가 더 크다고 볼 수 있음\r\nsum = add;\r\n```\r\n\r\n참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/\r\n"},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/sw.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/sw.json new file mode 100644 index 00000000..d0775d07 --- /dev/null +++ b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/sw.json @@ -0,0 +1 @@ +{"pageProps":{"posts":[{"title":"sw test","description":"정보처리기사 테스트","category":"정보처리기사","date":"2023-12-27","content":"\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n# TEST\r\n\r\n```typescript\r\n\r\n// 문자열\r\nlet str: string = \"hello\";\r\n\r\n// 숫자\r\nlet num: number = 100;\r\n\r\n// 배열\r\nlet arr: Array = [10,20,30];\r\nlet arr2: number[] = [10,20,30];\r\nlet arr3: Array = [\"hello\",\"hellololo\" ];\r\nlet arr4: [string, number] = [\"hello\", 182];\r\n\r\n// 객체\r\nlet obj:object = {name:\"hello\", age:29};\r\nlet person:{name: string, age:number};\r\n\r\n// Boolean\r\nlet isAvaliable: boolean = true;\r\n\r\n\r\n// 함수 선언\r\nparameter와 return 값에 대해 타입 선언 가능\r\nconst sum = (a:number, b:number):number => {\r\nreturn a+b;\r\n}\r\n\r\n// optional parameter일 경우 ?를 사용\r\n\r\nconst log = (time: string, result?: string, option?: string) => {\r\nconsole.log(time, result, option);\r\n}\r\nlog(\"2021-10-04\", \"success\");\r\n\r\n```\r\n\r\n# 인터페이스 (interface)\r\n\r\n### 자주 사용하는 타입들을 object 형태의 묶음으로 정의해 새로운 타입을 만들어 사용하는 기능\r\n\r\n```typescript\r\n\r\n// interface 선언\r\ninterface User {\r\nage: number;\r\nname: string;\r\n}\r\n\r\n// 변수 활용\r\nconst hanbbi: User = { age: 30, name: \"hello\"}\r\n\r\n\r\n// 함수 인자로의 활용\r\nconst getUser = (user:User){\r\nconsole.log(user);\r\n}\r\ngetUser({ age:10, name: \"hanbbi\" })\r\n\r\n\r\n\r\n// 함수 구조 활용\r\ninterface Sum {\r\n(a:number, b:number): number;\r\n}\r\n\r\nlet sumFinc: Sum:\r\nsumFunc = function(a: number, b: number): number {\r\nreturn a+b;\r\n}\r\n\r\n\r\n\r\n// 배열 활용\r\ninterface StringArray {\r\n[index:number]: string;\r\n}\r\n\r\nlet arr: StringArray = [\"a\", \"b\", \"c\"];\r\n\r\n\r\n// 객체 활용\r\ninterface StringRegexObject {\r\n[key: string]: RegExp;\r\n}\r\n\r\nconst obj: StringRegexObject {\r\ncssFile: /\\.css$/,\r\n jsFile: /\\.js$/\r\n}\r\n\r\n\r\n// interface 확장 (extends 사용)\r\ninterface Person {\r\nname: string;\r\nage:number;\r\n}\r\ninterface Developer extends Person {\r\nskill: string;\r\n}\r\n\r\nconst juniorDeveloper = {\r\nname: \"hanbbi\",\r\nage:100,\r\nskill: \"JS\"\r\n}\r\n\r\n```\r\n\r\n# 타입 별칭(type aliases)\r\n\r\n### 타입 키워드는 interface와 다르게 새로운 타입을 생성하는 것이 아닌 별칭을 부여하는 것으로, extends 키워드는 사용할 수 없음\r\n\r\n```typescript\r\n\r\n// 타입 별칭 선언 및 활용\r\ntype MyString = string;\r\nconst str: MyString = \"Hello dear\"\r\n\r\ntype Todo = {\r\nid: string;\r\ntitle: string;\r\ndone: boolean\r\n}\r\n\r\nconst getTodo(todo:Todo){\r\nconsole.log(todo);\r\n}\r\n\r\n```\r\n\r\n# 연산자 (Operator)\r\n\r\n## Uinon Type\r\n\r\n### 한 가지 이상의 type을 선언하고자 할 때 사용 가능. | 기호 사용\r\n\r\n```typescript\r\nconst logMessage = (value: string | number) => {\r\n if (typeof value === \"string\") {\r\n value.toString();\r\n } else if (typeof value === \"number\") {\r\n value.toLocaleString();\r\n } else {\r\n throw new TypeError(\"value must be string or number\");\r\n }\r\n};\r\nlogMessage(\"hello\");\r\nlogMessage(1000);\r\n```\r\n\r\n## intersection Type\r\n\r\n### 합집합과 같은 개념으로, 함수 호출의 경우 함수 인자에 명시한 type을 모두 제공해야 한다. & 기호 사용\r\n\r\n```typescript\r\n\r\ninterface Zoo {\r\nname: string;\r\nlocation: string\r\nprice: number;\r\n}\r\n\r\ninterface Animal {\r\nname: string;\r\ncount: number;\r\n}\r\n\r\nconst askZookeeper = ( value : Zoo & Animal ) => {\r\n // value 는\r\n { name:\"어린이대공원\", location: \"서울시 광진구\", price: 10000, count: 10000}\r\n // 와 같이 Zoo와 Animal이 모두 포함되는 인자를 줘야한다.\r\n}\r\n\r\n```\r\n\r\n# Enum\r\n\r\n### enum 키워드를 사용하면 일종의 default 값을 선언할 수 있다.\r\n\r\n```typescript\r\n\r\n// 숫자형 enum\r\n// 자동으로 0에서 1씩 증가하는 값을 부여\r\n\r\nenum Shoes {\r\nNike, // 0\r\nAdidas, // 1\r\nNewBalance //2\r\n}\r\nconst myShoes = Shoes.Nike; // 0\r\n\r\n문자형 enum\r\nenum Food {\r\ncake = \"케익\",\r\ncookie = \"쿠키\"\r\n}\r\nconst player = Food.cookie; // 쿠키\r\n\r\n```\r\n\r\n# 제네릭\r\n\r\n### 제네릭을 활용하면 인자를 넘겨 호출하는 시점에 타입을 결정할 수 있다. 제네릭 활용 시 동일한 기능을 하는 함수를 일일이 만들 필요가 없으며, 타입 추론에 있어 강점을 가진다.\r\n\r\n제네릭 선언\r\n와 같이 타입을 선언한다. 알파벳은 통상 T로 정해져 있다.\r\n\r\n```typescript\r\n\r\nconst logText = (text: T):T => {\r\nconsole.log(text);\r\nreturn text;\r\n}\r\nlogText(\"Hello hanbbi\");\r\n\r\n\r\n// interface에 제네릭 선언\r\n\r\ninterface Dropdown {\r\nvalue: T;\r\nselected: boolean;\r\n}\r\ncosnt obj: Dropdown = { value: \"hamburger\" , selected: true};\r\n\r\n```\r\n\r\n# 제네릭 타입 제한\r\n\r\n## 1. 배열 힌트\r\n\r\n```typescript\r\n\r\nconst logTextLength = (text: T[]): T[] =>{\r\nconsole.log(text.length);\r\ntext.forEach(text =>{\r\nconsole.log(text):\r\n});\r\n}\r\nlogTextLength([\"hi\", \"hello\"]);\r\n\r\n\r\n```\r\n\r\n## 2. 정의된 타입 이용(extends)과 keyof\r\n\r\n```typescript\r\n\r\ninterface ShoppingItem {\r\nname: string;\r\nprice: number;\r\nstock: number;\r\n}\r\n\r\nconst getShoppingItemOption(itemOption: T): T {\r\nreturn itemOption;\r\n}\r\n\r\n// \"name\", \"price\", \"stock\"만 인자로 가능\r\ngetShoppingItemOption(\"price\");\r\n\r\n```\r\n\r\n# 타입 추론 (Type inference)\r\n\r\n## 1. 기본 변수 타입 추론\r\n\r\n```typescript\r\n\r\n// string으로 추론\r\nlet a = \"abc\";\r\n\r\n// a: number로 추론\r\n// b: string으로 추론\r\n// return value는 string으로 추론\r\nconst getValue(a = 10) {\r\nlet b = \"hello\";\r\nreturn a + b;\r\n}\r\n\r\n\r\n```\r\n\r\n## 2. interface추론\r\n\r\n```typescript\r\n\r\n// interface 1개\r\ninterface Dropdown {\r\nvalue: T;\r\ntitle: string;\r\n}\r\nconst shoppingItem:Dropdown ={\r\nvalue: 10000;\r\ntitle: \"shoe\"\r\n}\r\n// interface 2개\r\ninterface Dropdown2 {\r\nvalue: T;\r\ntitle: string;\r\n}\r\ninterface DetailedDropdown extends Dropdown2{\r\ntag: K;\r\ndesc: string;\r\n}\r\nconst detailed: DetailedDropdown{\r\nvalue: \"10000\";\r\ntitle: \"shoe\",\r\ntag: \"10000\",\r\ndesc: \"description\"\r\n}\r\n\r\n```\r\n\r\n# 타입 단언 (Type assertion)\r\n\r\n### as 키워드를 사용해 타입을 정함으로써 typescript에게 타입을 알려줄 수 있다. 주로 DOM API를 조작할 떄 사용한다.\r\n\r\n```typescript\r\n// div가 있는지 장담할 수 없음, HTMLDivElement | null\r\n// 따라서 typescript에게 타입을 단언해 타입을 알려줄 수 있음.\r\nconst div = document.querySelector(\"div\") as HTMLDivElement;\r\ndiv.innerText = \"test\";\r\n```\r\n\r\n# 타입 가드 (Type guard)\r\n\r\n### union type을 사용하는 경우 공통된 속성만 접근 가능하며, 로직상 공통되지 않은 속성에 접근하고자 할 때 불편함을 해소하기 위해 타입 단언으로 공통되지 않은 속성에 접근하고자 하는 방법이 있지만, 코드 가독성을 위해 타입 가드 방법을 주로 사용한다.\r\n\r\n```typescript\r\nconst isDeveloper = (target: Developer | Humanoid): target is Developer => {\r\n return (target as Developer).skill !== undefined;\r\n};\r\nif (isDeveloper(tom)) {\r\n console.log(tom.name);\r\n console.log(tom.skill);\r\n} else {\r\n console.log(tom.name);\r\n console.log(tom.age);\r\n}\r\n```\r\n\r\n# 타입 호환 (Type compatibility)\r\n\r\n### TypeScript에서 더 큰 타입 구조를 갖는 변수에 작은 타입 구조를 갖는 변수를 할당 가능\r\n\r\n```typescript\r\nlet add = function (a: number) {\r\n // ...\r\n};\r\nlet sum = function (a: number, b: number) {\r\n // ...\r\n};\r\n// 아래는 에러가\r\n// add = sum;\r\n\r\n// 에러가 나지 않는 방식. sum의 구조가 더 크다고 볼 수 있음\r\nsum = add;\r\n```\r\n\r\n참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/\r\n"}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/typescript-syntax.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/typescript-syntax.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/typescript-syntax.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/typescript-syntax.json diff --git a/_next/data/OV20HWrLkXuRM8jaFvyc0/typescript.json b/_next/data/TS_Y-VMLzDgBVGe3Zk5Yz/typescript.json similarity index 100% rename from _next/data/OV20HWrLkXuRM8jaFvyc0/typescript.json rename to _next/data/TS_Y-VMLzDgBVGe3Zk5Yz/typescript.json diff --git a/_next/static/OV20HWrLkXuRM8jaFvyc0/_buildManifest.js b/_next/static/OV20HWrLkXuRM8jaFvyc0/_buildManifest.js deleted file mode 100644 index 70789b29..00000000 --- a/_next/static/OV20HWrLkXuRM8jaFvyc0/_buildManifest.js +++ /dev/null @@ -1 +0,0 @@ -self.__BUILD_MANIFEST=function(s){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,"static/chunks/pages/index-aa2231804f7a52cb.js"],"/404":["static/chunks/pages/404-52999431c1dae751.js"],"/_error":["static/chunks/pages/_error-409f831d3504c8f5.js"],"/cs":[s,"static/chunks/pages/cs-3fbdfdd36b3aab17.js"],"/css":[s,"static/chunks/pages/css-095d437005a74135.js"],"/github":[s,"static/chunks/pages/github-536b230c7719829d.js"],"/html":[s,"static/chunks/pages/html-7bf030290d72b9d3.js"],"/javascript":[s,"static/chunks/pages/javascript-7017a7cbb7261837.js"],"/nestjs":[s,"static/chunks/pages/nestjs-717f5e9bc1b19ae8.js"],"/nextjs":[s,"static/chunks/pages/nextjs-643ab0774bd18638.js"],"/profile":["static/chunks/17007de1-05a47c1b2f06088f.js","static/chunks/6728d85a-dc6a5e369adc0e05.js","static/chunks/727-92e754e7bfb8f94d.js","static/chunks/pages/profile-9afedf6ca3c87aba.js"],"/react":[s,"static/chunks/pages/react-6e85d367c24cca35.js"],"/redux":[s,"static/chunks/pages/redux-f7add576a0fbc86e.js"],"/search":[s,"static/chunks/pages/search-23f04ffbc62089c1.js"],"/typescript":[s,"static/chunks/pages/typescript-b96da9ef10a61bf8.js"],"/[slug]":["static/chunks/0-225a49c07b374aea.js","static/chunks/pages/[slug]-74f624fdf837c2ca.js"],sortedPages:["/","/404","/_app","/_error","/cs","/css","/github","/html","/javascript","/nestjs","/nextjs","/profile","/react","/redux","/search","/typescript","/[slug]"]}}("static/chunks/789-6e729330f76e9f3d.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/_next/static/OV20HWrLkXuRM8jaFvyc0/_ssgManifest.js b/_next/static/OV20HWrLkXuRM8jaFvyc0/_ssgManifest.js deleted file mode 100644 index eca3e06a..00000000 --- a/_next/static/OV20HWrLkXuRM8jaFvyc0/_ssgManifest.js +++ /dev/null @@ -1 +0,0 @@ -self.__SSG_MANIFEST=new Set(["\u002F","\u002F[slug]","\u002Fcs","\u002Fcss","\u002Fgithub","\u002Fhtml","\u002Fjavascript","\u002Fnestjs","\u002Fnextjs","\u002Freact","\u002Fredux","\u002Ftypescript"]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB() \ No newline at end of file diff --git a/_next/static/TS_Y-VMLzDgBVGe3Zk5Yz/_buildManifest.js b/_next/static/TS_Y-VMLzDgBVGe3Zk5Yz/_buildManifest.js new file mode 100644 index 00000000..cf14d422 --- /dev/null +++ b/_next/static/TS_Y-VMLzDgBVGe3Zk5Yz/_buildManifest.js @@ -0,0 +1 @@ +self.__BUILD_MANIFEST=function(s){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,"static/chunks/pages/index-aa2231804f7a52cb.js"],"/404":["static/chunks/pages/404-52999431c1dae751.js"],"/_error":["static/chunks/pages/_error-409f831d3504c8f5.js"],"/css":[s,"static/chunks/pages/css-095d437005a74135.js"],"/github":[s,"static/chunks/pages/github-536b230c7719829d.js"],"/html":[s,"static/chunks/pages/html-7bf030290d72b9d3.js"],"/javascript":[s,"static/chunks/pages/javascript-7017a7cbb7261837.js"],"/nestjs":[s,"static/chunks/pages/nestjs-717f5e9bc1b19ae8.js"],"/nextjs":[s,"static/chunks/pages/nextjs-643ab0774bd18638.js"],"/profile":["static/chunks/17007de1-05a47c1b2f06088f.js","static/chunks/6728d85a-dc6a5e369adc0e05.js","static/chunks/727-92e754e7bfb8f94d.js","static/chunks/pages/profile-9afedf6ca3c87aba.js"],"/react":[s,"static/chunks/pages/react-6e85d367c24cca35.js"],"/redux":[s,"static/chunks/pages/redux-f7add576a0fbc86e.js"],"/search":[s,"static/chunks/pages/search-23f04ffbc62089c1.js"],"/sw":[s,"static/chunks/pages/sw-9b1fdcf3f8a80910.js"],"/typescript":[s,"static/chunks/pages/typescript-b96da9ef10a61bf8.js"],"/[slug]":["static/chunks/0-225a49c07b374aea.js","static/chunks/pages/[slug]-74f624fdf837c2ca.js"],sortedPages:["/","/404","/_app","/_error","/css","/github","/html","/javascript","/nestjs","/nextjs","/profile","/react","/redux","/search","/sw","/typescript","/[slug]"]}}("static/chunks/789-6e729330f76e9f3d.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/_next/static/TS_Y-VMLzDgBVGe3Zk5Yz/_ssgManifest.js b/_next/static/TS_Y-VMLzDgBVGe3Zk5Yz/_ssgManifest.js new file mode 100644 index 00000000..4d75efa1 --- /dev/null +++ b/_next/static/TS_Y-VMLzDgBVGe3Zk5Yz/_ssgManifest.js @@ -0,0 +1 @@ +self.__SSG_MANIFEST=new Set(["\u002F","\u002F[slug]","\u002Fcss","\u002Fgithub","\u002Fhtml","\u002Fjavascript","\u002Fnestjs","\u002Fnextjs","\u002Freact","\u002Fredux","\u002Fsw","\u002Ftypescript"]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB() \ No newline at end of file diff --git a/_next/static/chunks/pages/_app-9ab2984ff460f65e.js b/_next/static/chunks/pages/_app-80b9c37206a02b35.js similarity index 99% rename from _next/static/chunks/pages/_app-9ab2984ff460f65e.js rename to _next/static/chunks/pages/_app-80b9c37206a02b35.js index a202492e..0ff188ed 100644 --- a/_next/static/chunks/pages/_app-9ab2984ff460f65e.js +++ b/_next/static/chunks/pages/_app-80b9c37206a02b35.js @@ -1,4 +1,4 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[888],{8679:function(e,t,r){"use strict";var n=r(59864),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},i={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},a={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},s={};function u(e){return n.isMemo(e)?a:s[e.$$typeof]||o}s[n.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},s[n.Memo]=a;var c=Object.defineProperty,l=Object.getOwnPropertyNames,f=Object.getOwnPropertySymbols,d=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,h=Object.prototype;e.exports=function e(t,r,n){if("string"!=typeof r){if(h){var o=p(r);o&&o!==h&&e(t,o,n)}var a=l(r);f&&(a=a.concat(f(r)));for(var s=u(t),m=u(r),g=0;g{}).then(()=>{if(e.parentNode){if("blur"===r&&a(!0),null==o?void 0:o.current){let t=new Event("load");Object.defineProperty(t,"target",{writable:!1,value:e});let r=!1,i=!1;o.current(n({},t,{nativeEvent:t,currentTarget:e,target:e,isDefaultPrevented:()=>r,isPropagationStopped:()=>i,persist:()=>{},preventDefault:()=>{r=!0,t.preventDefault()},stopPropagation:()=>{i=!0,t.stopPropagation()}}))}(null==i?void 0:i.current)&&i.current(e)}})}let y=s.forwardRef((e,t)=>{var{imgAttributes:r,heightInt:o,widthInt:i,qualityInt:u,className:c,imgStyle:l,blurStyle:f,isLazy:d,fill:p,placeholder:h,loading:m,srcString:y,config:v,unoptimized:b,loader:w,onLoadRef:x,onLoadingCompleteRef:S,setBlurComplete:E,setShowAltText:C,onLoad:O,onError:A}=e,j=a(e,["imgAttributes","heightInt","widthInt","qualityInt","className","imgStyle","blurStyle","isLazy","fill","placeholder","loading","srcString","config","unoptimized","loader","onLoadRef","onLoadingCompleteRef","setBlurComplete","setShowAltText","onLoad","onError"]);return m=d?"lazy":m,s.default.createElement(s.default.Fragment,null,s.default.createElement("img",Object.assign({},j,{loading:m,width:i,height:o,decoding:"async","data-nimg":p?"fill":"1",className:c,style:n({},l,f)},r,{ref:s.useCallback(e=>{t&&("function"==typeof t?t(e):"object"==typeof t&&(t.current=e)),e&&(A&&(e.src=e.src),e.complete&&g(e,y,h,x,S,E,b))},[y,h,x,S,E,A,b,t]),onLoad:e=>{let t=e.currentTarget;g(t,y,h,x,S,E,b)},onError:e=>{C(!0),"blur"===h&&E(!0),A&&A(e)}})))}),v=s.forwardRef((e,t)=>{let r,o;var i,{src:g,sizes:v,unoptimized:b=!1,priority:w=!1,loading:x,className:S,quality:E,width:C,height:O,fill:A,style:j,onLoad:k,onLoadingComplete:R,placeholder:P="empty",blurDataURL:_,layout:T,objectFit:I,objectPosition:B,lazyBoundary:L,lazyRoot:N}=e,M=a(e,["src","sizes","unoptimized","priority","loading","className","quality","width","height","fill","style","onLoad","onLoadingComplete","placeholder","blurDataURL","layout","objectFit","objectPosition","lazyBoundary","lazyRoot"]);let U=s.useContext(f.ImageConfigContext),z=s.useMemo(()=>{let e=p||U||l.imageConfigDefault,t=[...e.deviceSizes,...e.imageSizes].sort((e,t)=>e-t),r=e.deviceSizes.sort((e,t)=>e-t);return n({},e,{allSizes:t,deviceSizes:r})},[U]),F=M,D=F.loader||d.default;delete F.loader;let $="__next_img_default"in D;if($){if("custom"===z.loader)throw Error('Image with src "'.concat(g,'" is missing "loader" prop.')+"\nRead more: https://nextjs.org/docs/messages/next-image-missing-loader")}else{let e=D;D=t=>{let{config:r}=t,n=a(t,["config"]);return e(n)}}if(T){"fill"===T&&(A=!0);let e={intrinsic:{maxWidth:"100%",height:"auto"},responsive:{width:"100%",height:"auto"}}[T];e&&(j=n({},j,e));let t={responsive:"100vw",fill:"100vw"}[T];t&&!v&&(v=t)}let q="",H=m(C),W=m(O);if("object"==typeof(i=g)&&(h(i)||void 0!==i.src)){let e=h(g)?g.default:g;if(!e.src)throw Error("An object should only be passed to the image component src parameter if it comes from a static image import. It must include src. Received ".concat(JSON.stringify(e)));if(!e.height||!e.width)throw Error("An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received ".concat(JSON.stringify(e)));if(r=e.blurWidth,o=e.blurHeight,_=_||e.blurDataURL,q=e.src,!A){if(H||W){if(H&&!W){let t=H/e.width;W=Math.round(e.height*t)}else if(!H&&W){let t=W/e.height;H=Math.round(e.width*t)}}else H=e.width,W=e.height}}let Z=!w&&("lazy"===x||void 0===x);((g="string"==typeof g?g:q).startsWith("data:")||g.startsWith("blob:"))&&(b=!0,Z=!1),z.unoptimized&&(b=!0),$&&g.endsWith(".svg")&&!z.dangerouslyAllowSVG&&(b=!0);let[V,G]=s.useState(!1),[Y,J]=s.useState(!1),X=m(E),K=Object.assign(A?{position:"absolute",height:"100%",width:"100%",left:0,top:0,right:0,bottom:0,objectFit:I,objectPosition:B}:{},Y?{}:{color:"transparent"},j),Q="blur"===P&&_&&!V?{backgroundSize:K.objectFit||"cover",backgroundPosition:K.objectPosition||"50% 50%",backgroundRepeat:"no-repeat",backgroundImage:'url("data:image/svg+xml;charset=utf-8,'.concat(c.getImageBlurSvg({widthInt:H,heightInt:W,blurWidth:r,blurHeight:o,blurDataURL:_,objectFit:K.objectFit}),'")')}:{},ee=function(e){let{config:t,src:r,unoptimized:n,width:o,quality:i,sizes:a,loader:s}=e;if(n)return{src:r,srcSet:void 0,sizes:void 0};let{widths:u,kind:c}=function(e,t,r){let{deviceSizes:n,allSizes:o}=e;if(r){let e=/(^|\s)(1?\d?\d)vw/g,t=[];for(let n;n=e.exec(r);n)t.push(parseInt(n[2]));if(t.length){let e=.01*Math.min(...t);return{widths:o.filter(t=>t>=n[0]*e),kind:"w"}}return{widths:o,kind:"w"}}if("number"!=typeof t)return{widths:n,kind:"w"};let i=[...new Set([t,2*t].map(e=>o.find(t=>t>=e)||o[o.length-1]))];return{widths:i,kind:"x"}}(t,o,a),l=u.length-1;return{sizes:a||"w"!==c?a:"100vw",srcSet:u.map((e,n)=>"".concat(s({config:t,src:r,quality:i,width:e})," ").concat("w"===c?e:n+1).concat(c)).join(", "),src:s({config:t,src:r,quality:i,width:u[l]})}}({config:z,src:g,unoptimized:b,width:H,quality:X,sizes:v,loader:D}),et=g,er={imageSrcSet:ee.srcSet,imageSizes:ee.sizes,crossOrigin:F.crossOrigin},en=s.useRef(k);s.useEffect(()=>{en.current=k},[k]);let eo=s.useRef(R);s.useEffect(()=>{eo.current=R},[R]);let ei=n({isLazy:Z,imgAttributes:ee,heightInt:W,widthInt:H,qualityInt:X,className:S,imgStyle:K,blurStyle:Q,loading:x,config:z,fill:A,unoptimized:b,placeholder:P,loader:D,srcString:et,onLoadRef:en,onLoadingCompleteRef:eo,setBlurComplete:G,setShowAltText:J},F);return s.default.createElement(s.default.Fragment,null,s.default.createElement(y,Object.assign({},ei,{ref:t})),w?s.default.createElement(u.default,null,s.default.createElement("link",Object.assign({key:"__nimg-"+ee.src+ee.srcSet+ee.sizes,rel:"preload",as:"image",href:ee.srcSet?void 0:ee.src},er))):null)});t.default=v,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},31551:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=r(92648).Z,o=r(17273).Z,i=n(r(67294)),a=r(1978),s=r(62139),u=r(67795),c=r(670),l=r(54465),f=r(72692),d=r(48245),p=r(69246),h=r(10227),m=r(33468);let g=new Set;function y(e,t,r,n,o){if(o||s.isLocalURL(t)){if(!n.bypassPrefetchedCheck){let o=void 0!==n.locale?n.locale:"locale"in e?e.locale:void 0,i=t+"%"+r+"%"+o;if(g.has(i))return;g.add(i)}Promise.resolve(e.prefetch(t,r,n)).catch(e=>{})}}function v(e){return"string"==typeof e?e:u.formatUrl(e)}let b=i.default.forwardRef(function(e,t){let r,n;let{href:u,as:g,children:b,prefetch:w,passHref:x,replace:S,shallow:E,scroll:C,locale:O,onClick:A,onMouseEnter:j,onTouchStart:k,legacyBehavior:R=!1}=e,P=o(e,["href","as","children","prefetch","passHref","replace","shallow","scroll","locale","onClick","onMouseEnter","onTouchStart","legacyBehavior"]);r=b,R&&("string"==typeof r||"number"==typeof r)&&(r=i.default.createElement("a",null,r));let _=!1!==w,T=i.default.useContext(f.RouterContext),I=i.default.useContext(d.AppRouterContext),B=null!=T?T:I,L=!T,{href:N,as:M}=i.default.useMemo(()=>{if(!T){let e=v(u);return{href:e,as:g?v(g):e}}let[e,t]=a.resolveHref(T,u,!0);return{href:e,as:g?a.resolveHref(T,g):t||e}},[T,u,g]),U=i.default.useRef(N),z=i.default.useRef(M);R&&(n=i.default.Children.only(r));let F=R?n&&"object"==typeof n&&n.ref:t,[D,$,q]=p.useIntersection({rootMargin:"200px"}),H=i.default.useCallback(e=>{(z.current!==M||U.current!==N)&&(q(),z.current=M,U.current=N),D(e),F&&("function"==typeof F?F(e):"object"==typeof F&&(F.current=e))},[M,F,N,q,D]);i.default.useEffect(()=>{B&&$&&_&&y(B,N,M,{locale:O},L)},[M,N,$,O,_,null==T?void 0:T.locale,B,L]);let W={ref:H,onClick(e){R||"function"!=typeof A||A(e),R&&n.props&&"function"==typeof n.props.onClick&&n.props.onClick(e),B&&!e.defaultPrevented&&function(e,t,r,n,o,a,u,c,l,f){let{nodeName:d}=e.currentTarget,p="A"===d.toUpperCase();if(p&&(function(e){let t=e.currentTarget,r=t.getAttribute("target");return r&&"_self"!==r||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.nativeEvent&&2===e.nativeEvent.which}(e)||!l&&!s.isLocalURL(r)))return;e.preventDefault();let h=()=>{"beforePopState"in t?t[o?"replace":"push"](r,n,{shallow:a,locale:c,scroll:u}):t[o?"replace":"push"](n||r,{forceOptimisticNavigation:!f})};l?i.default.startTransition(h):h()}(e,B,N,M,S,E,C,O,L,_)},onMouseEnter(e){R||"function"!=typeof j||j(e),R&&n.props&&"function"==typeof n.props.onMouseEnter&&n.props.onMouseEnter(e),B&&(_||!L)&&y(B,N,M,{locale:O,priority:!0,bypassPrefetchedCheck:!0},L)},onTouchStart(e){R||"function"!=typeof k||k(e),R&&n.props&&"function"==typeof n.props.onTouchStart&&n.props.onTouchStart(e),B&&(_||!L)&&y(B,N,M,{locale:O,priority:!0,bypassPrefetchedCheck:!0},L)}};if(c.isAbsoluteUrl(M))W.href=M;else if(!R||x||"a"===n.type&&!("href"in n.props)){let e=void 0!==O?O:null==T?void 0:T.locale,t=(null==T?void 0:T.isLocaleDomain)&&h.getDomainLocale(M,e,null==T?void 0:T.locales,null==T?void 0:T.domainLocales);W.href=t||m.addBasePath(l.addLocale(M,e,null==T?void 0:T.defaultLocale))}return R?i.default.cloneElement(n,W):i.default.createElement("a",Object.assign({},P,W),r)});t.default=b,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},69246:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.useIntersection=function(e){let{rootRef:t,rootMargin:r,disabled:u}=e,c=u||!i,[l,f]=n.useState(!1),d=n.useRef(null),p=n.useCallback(e=>{d.current=e},[]);n.useEffect(()=>{if(i){if(c||l)return;let e=d.current;if(e&&e.tagName){let n=function(e,t,r){let{id:n,observer:o,elements:i}=function(e){let t;let r={root:e.root||null,margin:e.rootMargin||""},n=s.find(e=>e.root===r.root&&e.margin===r.margin);if(n&&(t=a.get(n)))return t;let o=new Map,i=new IntersectionObserver(e=>{e.forEach(e=>{let t=o.get(e.target),r=e.isIntersecting||e.intersectionRatio>0;t&&r&&t(r)})},e);return t={id:r,observer:i,elements:o},s.push(r),a.set(r,t),t}(r);return i.set(e,t),o.observe(e),function(){if(i.delete(e),o.unobserve(e),0===i.size){o.disconnect(),a.delete(n);let e=s.findIndex(e=>e.root===n.root&&e.margin===n.margin);e>-1&&s.splice(e,1)}}}(e,e=>e&&f(e),{root:null==t?void 0:t.current,rootMargin:r});return n}}else if(!l){let e=o.requestIdleCallback(()=>f(!0));return()=>o.cancelIdleCallback(e)}},[c,r,t,l,d.current]);let h=n.useCallback(()=>{f(!1)},[]);return[p,l,h]};var n=r(67294),o=r(44686);let i="function"==typeof IntersectionObserver,a=new Map,s=[];("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},2675:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getImageBlurSvg=function(e){let{widthInt:t,heightInt:r,blurWidth:n,blurHeight:o,blurDataURL:i,objectFit:a}=e,s=n||t,u=o||r,c=i.startsWith("data:image/jpeg")?"%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1'/%3E%3C/feComponentTransfer%3E%":"";return s&&u?"%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 ".concat(s," ").concat(u,"'%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='").concat(n&&o?"1":"20","'/%3E").concat(c,"%3C/filter%3E%3Cimage preserveAspectRatio='none' filter='url(%23b)' x='0' y='0' height='100%25' width='100%25' href='").concat(i,"'/%3E%3C/svg%3E"):"%3Csvg xmlns='http%3A//www.w3.org/2000/svg'%3E%3Cimage style='filter:blur(20px)' preserveAspectRatio='".concat("contain"===a?"xMidYMid":"cover"===a?"xMidYMid slice":"none","' x='0' y='0' height='100%25' width='100%25' href='").concat(i,"'/%3E%3C/svg%3E")}},89824:function(e,t){"use strict";function r(e){let{config:t,src:r,width:n,quality:o}=e;return"".concat(t.path,"?url=").concat(encodeURIComponent(r),"&w=").concat(n,"&q=").concat(o||75)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,r.__next_img_default=!0,t.default=r},54363:function(e,t,r){"use strict";r.d(t,{Oy:function(){return f},qZ:function(){return l}});var n=r(85893),o=r(67294),i=r(6154),a=r(21876).Buffer;let s={posts:[],setPosts:()=>{}},u=(0,o.createContext)(s.posts),c=(0,o.createContext)(s.setPosts),l=()=>{let e=(0,o.useContext)(u);return e},f=e=>{let{children:t}=e,[r,s]=(0,o.useState)([]),l="https://api.github.com/repos/LEE-YO-HAN/LEE-YO-HAN.github.io/contents/src/posting",f={headers:{"Content-Type":"application/json"}},d=async(e,t)=>{let r="".concat(l,"/").concat(t,"/").concat(e),n=await i.Z.get(r,f),o=a.from(n.data.content,"base64").toString(),s=o.match(/^---\n([\s\S]+?)\n---/);if(s){let e=JSON.stringify(s[0]).split("\\n"),t=e[4].match(/\d{4}-\d{2}-\d{2}/);if(t){let r={title:e[1].split("title: ")[1].replaceAll(" ","-"),description:e[2].split("description: ")[1],category:e[3].split("category: ")[1],date:t[0]};return r}}},p=async()=>{let e="".concat(l),t=await i.Z.get(e,f),r=[];for(let n of t.data){let t=await i.Z.get("".concat(e,"/").concat(n.name),f);for(let e of t.data){let t=await d(e.name,n.name);void 0!==t&&r.push(t)}}s(r)};return(0,o.useEffect)(()=>{p()},[]),(0,n.jsx)(u.Provider,{value:r,children:(0,n.jsx)(c.Provider,{value:s,children:t})})}},45272:function(e,t,r){"use strict";r.d(t,{X:function(){return i}});var n=r(67294),o=r(1852);let i=()=>{let[e,t]=(0,n.useState)(!1),r=(0,o.useMediaQuery)({maxWidth:480}),i=()=>{r?t(!0):t(!1)};return(0,n.useEffect)(()=>{i()},[r]),e}},20496:function(e,t,r){"use strict";r.r(t),r.d(t,{default:function(){return K}});var n=r(85893);r(79062);var o=r(11163),i=r(19521),a=r(7297),s=r(41664),u=r.n(s),c=r(45272),l=r(81873),f=r(89583);function d(){let e=(0,a.Z)(["\n display: flex;\n flex-direction: row;\n justify-content: center;\n justify-content: space-around;\n align-items: center;\n height: 79px;\n border-bottom: 1px solid ",";\n background-color: ",";\n & h1 {\n width: 50vw;\n text-align: center;\n cursor: pointer;\n }\n & a {\n transition: 0.3s;\n color: ",";\n &:hover {\n color: ",";\n }\n }\n"]);return d=function(){return e},e}function p(){let e=(0,a.Z)(["\n display: flex;\n font-size: 1.8rem;\n color: ",";\n &:hover {\n color: ",";\n }\n"]);return p=function(){return e},e}let h=()=>{let e=(0,c.X)(),t=e?{}:{display:"none"};return(0,n.jsxs)(m,{children:[(0,n.jsx)(g,{style:t,children:(0,n.jsx)(l.lrj,{})}),(0,n.jsx)("h1",{style:e?{fontSize:"1.4rem"}:{},children:(0,n.jsx)(u(),{href:"/",children:"Rain Sugar BangBang"})}),(0,n.jsx)(g,{style:t,children:(0,n.jsx)(f.U41,{})})]})},m=i.ZP.header.withConfig({componentId:"sc-fe6a8-0"})(d(),e=>e.theme.layoutBorderColor,e=>e.theme.componentBackground,e=>e.theme.componentFontColor,e=>e.theme.componentHover),g=i.ZP.span.withConfig({componentId:"sc-fe6a8-1"})(p(),e=>e.theme.componentFontColor,e=>e.theme.componentHover);function y(){let e=(0,a.Z)(["\n margin-left: 20px;\n"]);return y=function(){return e},e}function v(){let e=(0,a.Z)(["\n list-style: none;\n transition: 0.3s;\n & p:first-child {\n margin-bottom: 20px;\n font-weight: bold;\n font-size: 1.2rem;\n cursor: auto;\n }\n"]);return v=function(){return e},e}function b(){let e=(0,a.Z)(["\n margin: 20px 0 10px 0;\n font-weight: bold;\n font-size: 1.2rem;\n color: ",";\n cursor: auto;\n"]);return b=function(){return e},e}function w(){let e=(0,a.Z)(["\n margin-bottom: 7px;\n cursor: pointer;\n\n & a {\n color: ",";\n &:hover {\n font-weight: bold;\n color: white;\n }\n }\n"]);return w=function(){return e},e}let x=()=>{let e=(0,o.useRouter)(),t=e=>{let t="Simple Memo"===e?"github":e.toLowerCase();return{name:e,cateUrl:t}},r=[t("HTML"),t("CSS"),t("JavaScript"),t("TypeScript"),t("Redux"),t("React"),t("Nextjs")],i=[t("Nestjs")],a=[t("정보처리기사")],s=[t("Simple Memo")],c=["FE","BE","CS","ETC"];return(0,n.jsx)(S,{children:(0,n.jsxs)(E,{children:[(0,n.jsx)("p",{children:"Category"}),[r,i,a,s].map((t,r)=>(0,n.jsxs)("div",{children:[(0,n.jsx)(C,{children:c[r]}),t.map((t,r)=>{let{name:o,cateUrl:i}=t;return(0,n.jsx)(O,{children:(0,n.jsx)(u(),{href:"/".concat(i),style:"/".concat(i)===e.pathname||"/".concat(i)===e.asPath.split("-")[0]?{color:"white",fontWeight:"bold"}:{},children:o})},r)})]},r))]})})},S=i.ZP.nav.withConfig({componentId:"sc-32fda5e8-0"})(y()),E=i.ZP.ul.withConfig({componentId:"sc-32fda5e8-1"})(v()),C=i.ZP.p.withConfig({componentId:"sc-32fda5e8-2"})(b(),e=>e.theme.componentAccentFontColor),O=i.ZP.li.withConfig({componentId:"sc-32fda5e8-3"})(w(),e=>e.theme.componentFontColor);function A(){let e=(0,a.Z)(["\n padding: 20px 0;\n position: relative;\n top: 100px;\n right: 0;\n margin-left: 20px;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: flex-start;\n width: 200px;\n /* height: 350px; */\n height: max-content;\n color: ",";\n background-color: ",";\n border-radius: 10px;\n"]);return A=function(){return e},e}let j=()=>{let e=(0,c.X)();return(0,n.jsx)(k,{style:e?{display:"none"}:{},children:(0,n.jsx)(x,{})})},k=i.ZP.aside.withConfig({componentId:"sc-7eadfa91-0"})(A(),e=>e.theme.componentFontColor,e=>e.theme.componentBackground);var R=r(25675),P=r.n(R),_=r(63750),T=r(8193);function I(){let e=(0,a.Z)(["\n position: relative;\n top: 100px;\n left: 0;\n margin-right: 20px;\n display: flex;\n flex-direction: column;\n align-items: center;\n width: 200px;\n height: 350px;\n color: ",";\n background-color: ",";\n border-radius: 10px;\n"]);return I=function(){return e},e}function B(){let e=(0,a.Z)(["\n margin-top: 30px;\n display: flex;\n flex-direction: column;\n align-items: center;\n\n & img {\n border-radius: 50px;\n }\n & p {\n margin: 10px 15px 0 15px;\n font-weight: bold;\n font-size: 1.4rem;\n }\n & span {\n position: absolute;\n top: 8%;\n left: 25%;\n width: 100px;\n height: 100px;\n line-height: 100px;\n color: transparent;\n font-weight: bold;\n text-align: center;\n border-radius: 50%;\n transition: 0.2s;\n &:hover {\n color: white;\n background-color: #00000067;\n }\n }\n"]);return B=function(){return e},e}function L(){let e=(0,a.Z)(["\n margin: 20px 0;\n & a {\n margin-bottom: 10px;\n display: flex;\n align-items: center;\n color: ",";\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: 0.2s;\n\n &:hover {\n color: ",";\n }\n & svg {\n font-size: 15px;\n }\n & span {\n margin-left: 10px;\n }\n }\n"]);return L=function(){return e},e}let N=()=>{let e=(0,c.X)();return(0,n.jsxs)(M,{style:e?{display:"none"}:{},children:[(0,n.jsxs)(U,{children:[(0,n.jsxs)(u(),{href:"https://portfolio-sigma-wheat-63.vercel.app/",target:"_blank",children:[(0,n.jsx)(P(),{src:"/images/profileImage/cat.png",alt:"profile",width:100,height:100}),(0,n.jsx)("span",{children:"Go Profile!"})]}),(0,n.jsx)("p",{children:"Hanbbi"})]}),(0,n.jsxs)(z,{children:[(0,n.jsxs)(u(),{href:"https://github.com/LEE-YO-HAN",target:"_blank",children:[(0,n.jsx)(T.Dme,{}),(0,n.jsx)("span",{children:"yhl0078@gmail.com"})]}),(0,n.jsxs)(u(),{href:"https://github.com/LEE-YO-HAN",target:"_blank",children:[(0,n.jsx)(_.rFR,{}),(0,n.jsx)("span",{children:"Github"})]})]})]})},M=i.ZP.aside.withConfig({componentId:"sc-226f6af7-0"})(I(),e=>e.theme.componentFontColor,e=>e.theme.componentBackground),U=i.ZP.div.withConfig({componentId:"sc-226f6af7-1"})(B()),z=i.ZP.div.withConfig({componentId:"sc-226f6af7-2"})(L(),e=>e.theme.componentFontColor,e=>e.theme.componentHover);function F(){let e=(0,a.Z)(["\n margin: 0 auto;\n width: 100vw;\n height: 100vh;\n font-family: maplestory;\n background-color: ",";\n"]);return F=function(){return e},e}function D(){let e=(0,a.Z)(["\n display: flex;\n flex-direction: row;\n justify-content: center;\n background-color: ",";\n color: ",";\n"]);return D=function(){return e},e}function $(){let e=(0,a.Z)(["\n margin-top: 60px;\n display: flex;\n flex-direction: column;\n align-items: center;\n max-width: 660px;\n width: 100%;\n"]);return $=function(){return e},e}let q=e=>{let{children:t}=e;return(0,n.jsx)(n.Fragment,{children:(0,n.jsxs)(H,{children:[(0,n.jsx)(h,{}),(0,n.jsxs)(W,{children:[(0,n.jsx)(N,{}),(0,n.jsx)(Z,{children:t}),(0,n.jsx)(j,{})]})]})})},H=i.ZP.div.withConfig({componentId:"sc-c845a976-0"})(F(),e=>e.theme.backgroundColor),W=i.ZP.section.withConfig({componentId:"sc-c845a976-1"})(D(),e=>e.theme.backgroundColor,e=>e.theme.mainFontColor),Z=i.ZP.article.withConfig({componentId:"sc-c845a976-2"})($()),V={backgroundColor:"#111111",mainFontColor:"white",layoutBorderColor:"gray",componentFontColor:"#ffec43",componentSubFontColor:"lightgray",componentAccentFontColor:"orange",componentBackground:"black",componentShadow:"gold",componentHover:"white",loading:"gold",prevNextFont:"#000000",prevNextBackground:"#ffee52",prevNextHover:"#ffed47",noPrevNextFont:"#97904e",noPrevNextBackground:"#fff6a1",buttonFontColor:"gold",buttonBackgroundColor:"",activeButtonColor:"#000000",activeBackgroundColor:"#ffee52"};var G=r(17119),Y=r(67294);let J=()=>{let[e,t]=(0,Y.useState)(!1);return(0,Y.useEffect)(()=>{let e=()=>{t(!0)},r=()=>{t(!1)};return o.Router.events.on("routeChangeStart",e),o.Router.events.on("routeChangeComplete",r),o.Router.events.on("routeChangeError",r),()=>{o.Router.events.off("routeChangeStart",e),o.Router.events.off("routeChangeComplete",r),o.Router.events.off("routeChangeError",r)}},[]),e};var X=r(54363);function K(e){let{Component:t,pageProps:r}=e,a=J(),{pathname:s}=(0,o.useRouter)();return(0,n.jsx)(X.Oy,{children:(0,n.jsx)(i.f6,{theme:V,children:"/profile"!==s?(0,n.jsxs)(q,{children:[(0,n.jsx)(t,{...r}),a?(0,n.jsx)(G.T,{}):null]}):(0,n.jsx)(t,{...r})})})}},17119:function(e,t,r){"use strict";r.d(t,{T:function(){return s}});var n=r(7297),o=r(85893),i=r(19521);function a(){let e=(0,n.Z)(["\n display: block;\n width: 100%;\n height: 100%;\n position: fixed;\n left: 50%;\n top: 50%;\n z-index: 999;\n transform: translate(-50%, -50%);\n @keyframes spinner {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n & .spinner {\n box-sizing: border-box;\n position: absolute;\n top: 50%;\n left: 50%;\n width: 40px;\n height: 40px;\n margin-top: -32px;\n margin-left: -32px;\n border-radius: 50%;\n border: 3px solid transparent;\n border-top: 3px solid ",";\n border-right: 3px solid ",";\n border-bottom: 3px solid ",";\n animation: spinner 0.6s infinite;\n }\n"]);return a=function(){return e},e}let s=()=>(0,o.jsx)(u,{children:(0,o.jsx)("div",{className:"spinner"})}),u=i.ZP.div.withConfig({componentId:"sc-86b17283-0"})(a(),e=>e.theme.loading,e=>e.theme.loading,e=>e.theme.loading)},21876:function(e){!function(){var t={675:function(e,t){"use strict";t.byteLength=function(e){var t=u(e),r=t[0],n=t[1];return(r+n)*3/4-n},t.toByteArray=function(e){var t,r,i=u(e),a=i[0],s=i[1],c=new o((a+s)*3/4-s),l=0,f=s>0?a-4:a;for(r=0;r>16&255,c[l++]=t>>8&255,c[l++]=255&t;return 2===s&&(t=n[e.charCodeAt(r)]<<2|n[e.charCodeAt(r+1)]>>4,c[l++]=255&t),1===s&&(t=n[e.charCodeAt(r)]<<10|n[e.charCodeAt(r+1)]<<4|n[e.charCodeAt(r+2)]>>2,c[l++]=t>>8&255,c[l++]=255&t),c},t.fromByteArray=function(e){for(var t,n=e.length,o=n%3,i=[],a=0,s=n-o;a>18&63]+r[o>>12&63]+r[o>>6&63]+r[63&o]);return i.join("")}(e,a,a+16383>s?s:a+16383));return 1===o?i.push(r[(t=e[n-1])>>2]+r[t<<4&63]+"=="):2===o&&i.push(r[(t=(e[n-2]<<8)+e[n-1])>>10]+r[t>>4&63]+r[t<<2&63]+"="),i.join("")};for(var r=[],n=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=0,s=i.length;a0)throw Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");-1===r&&(r=t);var n=r===t?0:4-r%4;return[r,n]}n["-".charCodeAt(0)]=62,n["_".charCodeAt(0)]=63},72:function(e,t,r){"use strict";/*! +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[888],{8679:function(e,t,r){"use strict";var n=r(59864),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},i={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},a={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},s={};function u(e){return n.isMemo(e)?a:s[e.$$typeof]||o}s[n.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},s[n.Memo]=a;var c=Object.defineProperty,l=Object.getOwnPropertyNames,f=Object.getOwnPropertySymbols,d=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,h=Object.prototype;e.exports=function e(t,r,n){if("string"!=typeof r){if(h){var o=p(r);o&&o!==h&&e(t,o,n)}var a=l(r);f&&(a=a.concat(f(r)));for(var s=u(t),m=u(r),g=0;g{}).then(()=>{if(e.parentNode){if("blur"===r&&a(!0),null==o?void 0:o.current){let t=new Event("load");Object.defineProperty(t,"target",{writable:!1,value:e});let r=!1,i=!1;o.current(n({},t,{nativeEvent:t,currentTarget:e,target:e,isDefaultPrevented:()=>r,isPropagationStopped:()=>i,persist:()=>{},preventDefault:()=>{r=!0,t.preventDefault()},stopPropagation:()=>{i=!0,t.stopPropagation()}}))}(null==i?void 0:i.current)&&i.current(e)}})}let y=s.forwardRef((e,t)=>{var{imgAttributes:r,heightInt:o,widthInt:i,qualityInt:u,className:c,imgStyle:l,blurStyle:f,isLazy:d,fill:p,placeholder:h,loading:m,srcString:y,config:v,unoptimized:b,loader:w,onLoadRef:x,onLoadingCompleteRef:S,setBlurComplete:E,setShowAltText:C,onLoad:O,onError:A}=e,j=a(e,["imgAttributes","heightInt","widthInt","qualityInt","className","imgStyle","blurStyle","isLazy","fill","placeholder","loading","srcString","config","unoptimized","loader","onLoadRef","onLoadingCompleteRef","setBlurComplete","setShowAltText","onLoad","onError"]);return m=d?"lazy":m,s.default.createElement(s.default.Fragment,null,s.default.createElement("img",Object.assign({},j,{loading:m,width:i,height:o,decoding:"async","data-nimg":p?"fill":"1",className:c,style:n({},l,f)},r,{ref:s.useCallback(e=>{t&&("function"==typeof t?t(e):"object"==typeof t&&(t.current=e)),e&&(A&&(e.src=e.src),e.complete&&g(e,y,h,x,S,E,b))},[y,h,x,S,E,A,b,t]),onLoad:e=>{let t=e.currentTarget;g(t,y,h,x,S,E,b)},onError:e=>{C(!0),"blur"===h&&E(!0),A&&A(e)}})))}),v=s.forwardRef((e,t)=>{let r,o;var i,{src:g,sizes:v,unoptimized:b=!1,priority:w=!1,loading:x,className:S,quality:E,width:C,height:O,fill:A,style:j,onLoad:k,onLoadingComplete:R,placeholder:P="empty",blurDataURL:_,layout:T,objectFit:I,objectPosition:B,lazyBoundary:L,lazyRoot:N}=e,M=a(e,["src","sizes","unoptimized","priority","loading","className","quality","width","height","fill","style","onLoad","onLoadingComplete","placeholder","blurDataURL","layout","objectFit","objectPosition","lazyBoundary","lazyRoot"]);let U=s.useContext(f.ImageConfigContext),z=s.useMemo(()=>{let e=p||U||l.imageConfigDefault,t=[...e.deviceSizes,...e.imageSizes].sort((e,t)=>e-t),r=e.deviceSizes.sort((e,t)=>e-t);return n({},e,{allSizes:t,deviceSizes:r})},[U]),F=M,D=F.loader||d.default;delete F.loader;let $="__next_img_default"in D;if($){if("custom"===z.loader)throw Error('Image with src "'.concat(g,'" is missing "loader" prop.')+"\nRead more: https://nextjs.org/docs/messages/next-image-missing-loader")}else{let e=D;D=t=>{let{config:r}=t,n=a(t,["config"]);return e(n)}}if(T){"fill"===T&&(A=!0);let e={intrinsic:{maxWidth:"100%",height:"auto"},responsive:{width:"100%",height:"auto"}}[T];e&&(j=n({},j,e));let t={responsive:"100vw",fill:"100vw"}[T];t&&!v&&(v=t)}let q="",H=m(C),W=m(O);if("object"==typeof(i=g)&&(h(i)||void 0!==i.src)){let e=h(g)?g.default:g;if(!e.src)throw Error("An object should only be passed to the image component src parameter if it comes from a static image import. It must include src. Received ".concat(JSON.stringify(e)));if(!e.height||!e.width)throw Error("An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received ".concat(JSON.stringify(e)));if(r=e.blurWidth,o=e.blurHeight,_=_||e.blurDataURL,q=e.src,!A){if(H||W){if(H&&!W){let t=H/e.width;W=Math.round(e.height*t)}else if(!H&&W){let t=W/e.height;H=Math.round(e.width*t)}}else H=e.width,W=e.height}}let Z=!w&&("lazy"===x||void 0===x);((g="string"==typeof g?g:q).startsWith("data:")||g.startsWith("blob:"))&&(b=!0,Z=!1),z.unoptimized&&(b=!0),$&&g.endsWith(".svg")&&!z.dangerouslyAllowSVG&&(b=!0);let[V,G]=s.useState(!1),[Y,J]=s.useState(!1),X=m(E),K=Object.assign(A?{position:"absolute",height:"100%",width:"100%",left:0,top:0,right:0,bottom:0,objectFit:I,objectPosition:B}:{},Y?{}:{color:"transparent"},j),Q="blur"===P&&_&&!V?{backgroundSize:K.objectFit||"cover",backgroundPosition:K.objectPosition||"50% 50%",backgroundRepeat:"no-repeat",backgroundImage:'url("data:image/svg+xml;charset=utf-8,'.concat(c.getImageBlurSvg({widthInt:H,heightInt:W,blurWidth:r,blurHeight:o,blurDataURL:_,objectFit:K.objectFit}),'")')}:{},ee=function(e){let{config:t,src:r,unoptimized:n,width:o,quality:i,sizes:a,loader:s}=e;if(n)return{src:r,srcSet:void 0,sizes:void 0};let{widths:u,kind:c}=function(e,t,r){let{deviceSizes:n,allSizes:o}=e;if(r){let e=/(^|\s)(1?\d?\d)vw/g,t=[];for(let n;n=e.exec(r);n)t.push(parseInt(n[2]));if(t.length){let e=.01*Math.min(...t);return{widths:o.filter(t=>t>=n[0]*e),kind:"w"}}return{widths:o,kind:"w"}}if("number"!=typeof t)return{widths:n,kind:"w"};let i=[...new Set([t,2*t].map(e=>o.find(t=>t>=e)||o[o.length-1]))];return{widths:i,kind:"x"}}(t,o,a),l=u.length-1;return{sizes:a||"w"!==c?a:"100vw",srcSet:u.map((e,n)=>"".concat(s({config:t,src:r,quality:i,width:e})," ").concat("w"===c?e:n+1).concat(c)).join(", "),src:s({config:t,src:r,quality:i,width:u[l]})}}({config:z,src:g,unoptimized:b,width:H,quality:X,sizes:v,loader:D}),et=g,er={imageSrcSet:ee.srcSet,imageSizes:ee.sizes,crossOrigin:F.crossOrigin},en=s.useRef(k);s.useEffect(()=>{en.current=k},[k]);let eo=s.useRef(R);s.useEffect(()=>{eo.current=R},[R]);let ei=n({isLazy:Z,imgAttributes:ee,heightInt:W,widthInt:H,qualityInt:X,className:S,imgStyle:K,blurStyle:Q,loading:x,config:z,fill:A,unoptimized:b,placeholder:P,loader:D,srcString:et,onLoadRef:en,onLoadingCompleteRef:eo,setBlurComplete:G,setShowAltText:J},F);return s.default.createElement(s.default.Fragment,null,s.default.createElement(y,Object.assign({},ei,{ref:t})),w?s.default.createElement(u.default,null,s.default.createElement("link",Object.assign({key:"__nimg-"+ee.src+ee.srcSet+ee.sizes,rel:"preload",as:"image",href:ee.srcSet?void 0:ee.src},er))):null)});t.default=v,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},31551:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=r(92648).Z,o=r(17273).Z,i=n(r(67294)),a=r(1978),s=r(62139),u=r(67795),c=r(670),l=r(54465),f=r(72692),d=r(48245),p=r(69246),h=r(10227),m=r(33468);let g=new Set;function y(e,t,r,n,o){if(o||s.isLocalURL(t)){if(!n.bypassPrefetchedCheck){let o=void 0!==n.locale?n.locale:"locale"in e?e.locale:void 0,i=t+"%"+r+"%"+o;if(g.has(i))return;g.add(i)}Promise.resolve(e.prefetch(t,r,n)).catch(e=>{})}}function v(e){return"string"==typeof e?e:u.formatUrl(e)}let b=i.default.forwardRef(function(e,t){let r,n;let{href:u,as:g,children:b,prefetch:w,passHref:x,replace:S,shallow:E,scroll:C,locale:O,onClick:A,onMouseEnter:j,onTouchStart:k,legacyBehavior:R=!1}=e,P=o(e,["href","as","children","prefetch","passHref","replace","shallow","scroll","locale","onClick","onMouseEnter","onTouchStart","legacyBehavior"]);r=b,R&&("string"==typeof r||"number"==typeof r)&&(r=i.default.createElement("a",null,r));let _=!1!==w,T=i.default.useContext(f.RouterContext),I=i.default.useContext(d.AppRouterContext),B=null!=T?T:I,L=!T,{href:N,as:M}=i.default.useMemo(()=>{if(!T){let e=v(u);return{href:e,as:g?v(g):e}}let[e,t]=a.resolveHref(T,u,!0);return{href:e,as:g?a.resolveHref(T,g):t||e}},[T,u,g]),U=i.default.useRef(N),z=i.default.useRef(M);R&&(n=i.default.Children.only(r));let F=R?n&&"object"==typeof n&&n.ref:t,[D,$,q]=p.useIntersection({rootMargin:"200px"}),H=i.default.useCallback(e=>{(z.current!==M||U.current!==N)&&(q(),z.current=M,U.current=N),D(e),F&&("function"==typeof F?F(e):"object"==typeof F&&(F.current=e))},[M,F,N,q,D]);i.default.useEffect(()=>{B&&$&&_&&y(B,N,M,{locale:O},L)},[M,N,$,O,_,null==T?void 0:T.locale,B,L]);let W={ref:H,onClick(e){R||"function"!=typeof A||A(e),R&&n.props&&"function"==typeof n.props.onClick&&n.props.onClick(e),B&&!e.defaultPrevented&&function(e,t,r,n,o,a,u,c,l,f){let{nodeName:d}=e.currentTarget,p="A"===d.toUpperCase();if(p&&(function(e){let t=e.currentTarget,r=t.getAttribute("target");return r&&"_self"!==r||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.nativeEvent&&2===e.nativeEvent.which}(e)||!l&&!s.isLocalURL(r)))return;e.preventDefault();let h=()=>{"beforePopState"in t?t[o?"replace":"push"](r,n,{shallow:a,locale:c,scroll:u}):t[o?"replace":"push"](n||r,{forceOptimisticNavigation:!f})};l?i.default.startTransition(h):h()}(e,B,N,M,S,E,C,O,L,_)},onMouseEnter(e){R||"function"!=typeof j||j(e),R&&n.props&&"function"==typeof n.props.onMouseEnter&&n.props.onMouseEnter(e),B&&(_||!L)&&y(B,N,M,{locale:O,priority:!0,bypassPrefetchedCheck:!0},L)},onTouchStart(e){R||"function"!=typeof k||k(e),R&&n.props&&"function"==typeof n.props.onTouchStart&&n.props.onTouchStart(e),B&&(_||!L)&&y(B,N,M,{locale:O,priority:!0,bypassPrefetchedCheck:!0},L)}};if(c.isAbsoluteUrl(M))W.href=M;else if(!R||x||"a"===n.type&&!("href"in n.props)){let e=void 0!==O?O:null==T?void 0:T.locale,t=(null==T?void 0:T.isLocaleDomain)&&h.getDomainLocale(M,e,null==T?void 0:T.locales,null==T?void 0:T.domainLocales);W.href=t||m.addBasePath(l.addLocale(M,e,null==T?void 0:T.defaultLocale))}return R?i.default.cloneElement(n,W):i.default.createElement("a",Object.assign({},P,W),r)});t.default=b,("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},69246:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.useIntersection=function(e){let{rootRef:t,rootMargin:r,disabled:u}=e,c=u||!i,[l,f]=n.useState(!1),d=n.useRef(null),p=n.useCallback(e=>{d.current=e},[]);n.useEffect(()=>{if(i){if(c||l)return;let e=d.current;if(e&&e.tagName){let n=function(e,t,r){let{id:n,observer:o,elements:i}=function(e){let t;let r={root:e.root||null,margin:e.rootMargin||""},n=s.find(e=>e.root===r.root&&e.margin===r.margin);if(n&&(t=a.get(n)))return t;let o=new Map,i=new IntersectionObserver(e=>{e.forEach(e=>{let t=o.get(e.target),r=e.isIntersecting||e.intersectionRatio>0;t&&r&&t(r)})},e);return t={id:r,observer:i,elements:o},s.push(r),a.set(r,t),t}(r);return i.set(e,t),o.observe(e),function(){if(i.delete(e),o.unobserve(e),0===i.size){o.disconnect(),a.delete(n);let e=s.findIndex(e=>e.root===n.root&&e.margin===n.margin);e>-1&&s.splice(e,1)}}}(e,e=>e&&f(e),{root:null==t?void 0:t.current,rootMargin:r});return n}}else if(!l){let e=o.requestIdleCallback(()=>f(!0));return()=>o.cancelIdleCallback(e)}},[c,r,t,l,d.current]);let h=n.useCallback(()=>{f(!1)},[]);return[p,l,h]};var n=r(67294),o=r(44686);let i="function"==typeof IntersectionObserver,a=new Map,s=[];("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},2675:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getImageBlurSvg=function(e){let{widthInt:t,heightInt:r,blurWidth:n,blurHeight:o,blurDataURL:i,objectFit:a}=e,s=n||t,u=o||r,c=i.startsWith("data:image/jpeg")?"%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1'/%3E%3C/feComponentTransfer%3E%":"";return s&&u?"%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 ".concat(s," ").concat(u,"'%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='").concat(n&&o?"1":"20","'/%3E").concat(c,"%3C/filter%3E%3Cimage preserveAspectRatio='none' filter='url(%23b)' x='0' y='0' height='100%25' width='100%25' href='").concat(i,"'/%3E%3C/svg%3E"):"%3Csvg xmlns='http%3A//www.w3.org/2000/svg'%3E%3Cimage style='filter:blur(20px)' preserveAspectRatio='".concat("contain"===a?"xMidYMid":"cover"===a?"xMidYMid slice":"none","' x='0' y='0' height='100%25' width='100%25' href='").concat(i,"'/%3E%3C/svg%3E")}},89824:function(e,t){"use strict";function r(e){let{config:t,src:r,width:n,quality:o}=e;return"".concat(t.path,"?url=").concat(encodeURIComponent(r),"&w=").concat(n,"&q=").concat(o||75)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,r.__next_img_default=!0,t.default=r},54363:function(e,t,r){"use strict";r.d(t,{Oy:function(){return f},qZ:function(){return l}});var n=r(85893),o=r(67294),i=r(6154),a=r(21876).Buffer;let s={posts:[],setPosts:()=>{}},u=(0,o.createContext)(s.posts),c=(0,o.createContext)(s.setPosts),l=()=>{let e=(0,o.useContext)(u);return e},f=e=>{let{children:t}=e,[r,s]=(0,o.useState)([]),l="https://api.github.com/repos/LEE-YO-HAN/LEE-YO-HAN.github.io/contents/src/posting",f={headers:{"Content-Type":"application/json"}},d=async(e,t)=>{let r="".concat(l,"/").concat(t,"/").concat(e),n=await i.Z.get(r,f),o=a.from(n.data.content,"base64").toString(),s=o.match(/^---\n([\s\S]+?)\n---/);if(s){let e=JSON.stringify(s[0]).split("\\n"),t=e[4].match(/\d{4}-\d{2}-\d{2}/);if(t){let r={title:e[1].split("title: ")[1].replaceAll(" ","-"),description:e[2].split("description: ")[1],category:e[3].split("category: ")[1],date:t[0]};return r}}},p=async()=>{let e="".concat(l),t=await i.Z.get(e,f),r=[];for(let n of t.data){let t=await i.Z.get("".concat(e,"/").concat(n.name),f);for(let e of t.data){let t=await d(e.name,n.name);void 0!==t&&r.push(t)}}s(r)};return(0,o.useEffect)(()=>{p()},[]),(0,n.jsx)(u.Provider,{value:r,children:(0,n.jsx)(c.Provider,{value:s,children:t})})}},45272:function(e,t,r){"use strict";r.d(t,{X:function(){return i}});var n=r(67294),o=r(1852);let i=()=>{let[e,t]=(0,n.useState)(!1),r=(0,o.useMediaQuery)({maxWidth:480}),i=()=>{r?t(!0):t(!1)};return(0,n.useEffect)(()=>{i()},[r]),e}},20496:function(e,t,r){"use strict";r.r(t),r.d(t,{default:function(){return K}});var n=r(85893);r(79062);var o=r(11163),i=r(19521),a=r(7297),s=r(41664),u=r.n(s),c=r(45272),l=r(81873),f=r(89583);function d(){let e=(0,a.Z)(["\n display: flex;\n flex-direction: row;\n justify-content: center;\n justify-content: space-around;\n align-items: center;\n height: 79px;\n border-bottom: 1px solid ",";\n background-color: ",";\n & h1 {\n width: 50vw;\n text-align: center;\n cursor: pointer;\n }\n & a {\n transition: 0.3s;\n color: ",";\n &:hover {\n color: ",";\n }\n }\n"]);return d=function(){return e},e}function p(){let e=(0,a.Z)(["\n display: flex;\n font-size: 1.8rem;\n color: ",";\n &:hover {\n color: ",";\n }\n"]);return p=function(){return e},e}let h=()=>{let e=(0,c.X)(),t=e?{}:{display:"none"};return(0,n.jsxs)(m,{children:[(0,n.jsx)(g,{style:t,children:(0,n.jsx)(l.lrj,{})}),(0,n.jsx)("h1",{style:e?{fontSize:"1.4rem"}:{},children:(0,n.jsx)(u(),{href:"/",children:"Rain Sugar BangBang"})}),(0,n.jsx)(g,{style:t,children:(0,n.jsx)(f.U41,{})})]})},m=i.ZP.header.withConfig({componentId:"sc-fe6a8-0"})(d(),e=>e.theme.layoutBorderColor,e=>e.theme.componentBackground,e=>e.theme.componentFontColor,e=>e.theme.componentHover),g=i.ZP.span.withConfig({componentId:"sc-fe6a8-1"})(p(),e=>e.theme.componentFontColor,e=>e.theme.componentHover);function y(){let e=(0,a.Z)(["\n margin-left: 20px;\n"]);return y=function(){return e},e}function v(){let e=(0,a.Z)(["\n list-style: none;\n transition: 0.3s;\n & p:first-child {\n margin-bottom: 20px;\n font-weight: bold;\n font-size: 1.2rem;\n cursor: auto;\n }\n"]);return v=function(){return e},e}function b(){let e=(0,a.Z)(["\n margin: 20px 0 10px 0;\n font-weight: bold;\n font-size: 1.2rem;\n color: ",";\n cursor: auto;\n"]);return b=function(){return e},e}function w(){let e=(0,a.Z)(["\n margin-bottom: 7px;\n cursor: pointer;\n\n & a {\n color: ",";\n &:hover {\n font-weight: bold;\n color: white;\n }\n }\n"]);return w=function(){return e},e}let x=()=>{let e=(0,o.useRouter)(),t=e=>{let t="Simple Memo"===e?"github":e.toLowerCase();return{name:e,cateUrl:t}},r=[t("HTML"),t("CSS"),t("JavaScript"),t("TypeScript"),t("Redux"),t("React"),t("Nextjs")],i=[t("Nestjs")],a=[t("정보처리기사")],s=[t("Simple Memo")],c=["FE","BE","CS","ETC"];return(0,n.jsx)(S,{children:(0,n.jsxs)(E,{children:[(0,n.jsx)("p",{children:"Category"}),[r,i,a,s].map((t,r)=>(0,n.jsxs)("div",{children:[(0,n.jsx)(C,{children:c[r]}),t.map((t,r)=>{let{name:o,cateUrl:i}=t;return(0,n.jsx)(O,{children:(0,n.jsx)(u(),{href:"/".concat(i),style:"/".concat(i)===e.pathname||"/".concat(i)===e.asPath.split("-")[0]?{color:"white",fontWeight:"bold"}:{},children:o})},r)})]},r))]})})},S=i.ZP.nav.withConfig({componentId:"sc-c7d31db6-0"})(y()),E=i.ZP.ul.withConfig({componentId:"sc-c7d31db6-1"})(v()),C=i.ZP.p.withConfig({componentId:"sc-c7d31db6-2"})(b(),e=>e.theme.componentAccentFontColor),O=i.ZP.li.withConfig({componentId:"sc-c7d31db6-3"})(w(),e=>e.theme.componentFontColor);function A(){let e=(0,a.Z)(["\n padding: 20px 0;\n position: relative;\n top: 100px;\n right: 0;\n margin-left: 20px;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: flex-start;\n width: 200px;\n /* height: 350px; */\n height: max-content;\n color: ",";\n background-color: ",";\n border-radius: 10px;\n"]);return A=function(){return e},e}let j=()=>{let e=(0,c.X)();return(0,n.jsx)(k,{style:e?{display:"none"}:{},children:(0,n.jsx)(x,{})})},k=i.ZP.aside.withConfig({componentId:"sc-7eadfa91-0"})(A(),e=>e.theme.componentFontColor,e=>e.theme.componentBackground);var R=r(25675),P=r.n(R),_=r(63750),T=r(8193);function I(){let e=(0,a.Z)(["\n position: relative;\n top: 100px;\n left: 0;\n margin-right: 20px;\n display: flex;\n flex-direction: column;\n align-items: center;\n width: 200px;\n height: 350px;\n color: ",";\n background-color: ",";\n border-radius: 10px;\n"]);return I=function(){return e},e}function B(){let e=(0,a.Z)(["\n margin-top: 30px;\n display: flex;\n flex-direction: column;\n align-items: center;\n\n & img {\n border-radius: 50px;\n }\n & p {\n margin: 10px 15px 0 15px;\n font-weight: bold;\n font-size: 1.4rem;\n }\n & span {\n position: absolute;\n top: 8%;\n left: 25%;\n width: 100px;\n height: 100px;\n line-height: 100px;\n color: transparent;\n font-weight: bold;\n text-align: center;\n border-radius: 50%;\n transition: 0.2s;\n &:hover {\n color: white;\n background-color: #00000067;\n }\n }\n"]);return B=function(){return e},e}function L(){let e=(0,a.Z)(["\n margin: 20px 0;\n & a {\n margin-bottom: 10px;\n display: flex;\n align-items: center;\n color: ",";\n font-weight: bold;\n text-decoration: none;\n cursor: pointer;\n transition: 0.2s;\n\n &:hover {\n color: ",";\n }\n & svg {\n font-size: 15px;\n }\n & span {\n margin-left: 10px;\n }\n }\n"]);return L=function(){return e},e}let N=()=>{let e=(0,c.X)();return(0,n.jsxs)(M,{style:e?{display:"none"}:{},children:[(0,n.jsxs)(U,{children:[(0,n.jsxs)(u(),{href:"https://portfolio-sigma-wheat-63.vercel.app/",target:"_blank",children:[(0,n.jsx)(P(),{src:"/images/profileImage/cat.png",alt:"profile",width:100,height:100}),(0,n.jsx)("span",{children:"Go Profile!"})]}),(0,n.jsx)("p",{children:"Hanbbi"})]}),(0,n.jsxs)(z,{children:[(0,n.jsxs)(u(),{href:"https://github.com/LEE-YO-HAN",target:"_blank",children:[(0,n.jsx)(T.Dme,{}),(0,n.jsx)("span",{children:"yhl0078@gmail.com"})]}),(0,n.jsxs)(u(),{href:"https://github.com/LEE-YO-HAN",target:"_blank",children:[(0,n.jsx)(_.rFR,{}),(0,n.jsx)("span",{children:"Github"})]})]})]})},M=i.ZP.aside.withConfig({componentId:"sc-226f6af7-0"})(I(),e=>e.theme.componentFontColor,e=>e.theme.componentBackground),U=i.ZP.div.withConfig({componentId:"sc-226f6af7-1"})(B()),z=i.ZP.div.withConfig({componentId:"sc-226f6af7-2"})(L(),e=>e.theme.componentFontColor,e=>e.theme.componentHover);function F(){let e=(0,a.Z)(["\n margin: 0 auto;\n width: 100vw;\n height: 100vh;\n font-family: maplestory;\n background-color: ",";\n"]);return F=function(){return e},e}function D(){let e=(0,a.Z)(["\n display: flex;\n flex-direction: row;\n justify-content: center;\n background-color: ",";\n color: ",";\n"]);return D=function(){return e},e}function $(){let e=(0,a.Z)(["\n margin-top: 60px;\n display: flex;\n flex-direction: column;\n align-items: center;\n max-width: 660px;\n width: 100%;\n"]);return $=function(){return e},e}let q=e=>{let{children:t}=e;return(0,n.jsx)(n.Fragment,{children:(0,n.jsxs)(H,{children:[(0,n.jsx)(h,{}),(0,n.jsxs)(W,{children:[(0,n.jsx)(N,{}),(0,n.jsx)(Z,{children:t}),(0,n.jsx)(j,{})]})]})})},H=i.ZP.div.withConfig({componentId:"sc-c845a976-0"})(F(),e=>e.theme.backgroundColor),W=i.ZP.section.withConfig({componentId:"sc-c845a976-1"})(D(),e=>e.theme.backgroundColor,e=>e.theme.mainFontColor),Z=i.ZP.article.withConfig({componentId:"sc-c845a976-2"})($()),V={backgroundColor:"#111111",mainFontColor:"white",layoutBorderColor:"gray",componentFontColor:"#ffec43",componentSubFontColor:"lightgray",componentAccentFontColor:"orange",componentBackground:"black",componentShadow:"gold",componentHover:"white",loading:"gold",prevNextFont:"#000000",prevNextBackground:"#ffee52",prevNextHover:"#ffed47",noPrevNextFont:"#97904e",noPrevNextBackground:"#fff6a1",buttonFontColor:"gold",buttonBackgroundColor:"",activeButtonColor:"#000000",activeBackgroundColor:"#ffee52"};var G=r(17119),Y=r(67294);let J=()=>{let[e,t]=(0,Y.useState)(!1);return(0,Y.useEffect)(()=>{let e=()=>{t(!0)},r=()=>{t(!1)};return o.Router.events.on("routeChangeStart",e),o.Router.events.on("routeChangeComplete",r),o.Router.events.on("routeChangeError",r),()=>{o.Router.events.off("routeChangeStart",e),o.Router.events.off("routeChangeComplete",r),o.Router.events.off("routeChangeError",r)}},[]),e};var X=r(54363);function K(e){let{Component:t,pageProps:r}=e,a=J(),{pathname:s}=(0,o.useRouter)();return(0,n.jsx)(X.Oy,{children:(0,n.jsx)(i.f6,{theme:V,children:"/profile"!==s?(0,n.jsxs)(q,{children:[(0,n.jsx)(t,{...r}),a?(0,n.jsx)(G.T,{}):null]}):(0,n.jsx)(t,{...r})})})}},17119:function(e,t,r){"use strict";r.d(t,{T:function(){return s}});var n=r(7297),o=r(85893),i=r(19521);function a(){let e=(0,n.Z)(["\n display: block;\n width: 100%;\n height: 100%;\n position: fixed;\n left: 50%;\n top: 50%;\n z-index: 999;\n transform: translate(-50%, -50%);\n @keyframes spinner {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n & .spinner {\n box-sizing: border-box;\n position: absolute;\n top: 50%;\n left: 50%;\n width: 40px;\n height: 40px;\n margin-top: -32px;\n margin-left: -32px;\n border-radius: 50%;\n border: 3px solid transparent;\n border-top: 3px solid ",";\n border-right: 3px solid ",";\n border-bottom: 3px solid ",";\n animation: spinner 0.6s infinite;\n }\n"]);return a=function(){return e},e}let s=()=>(0,o.jsx)(u,{children:(0,o.jsx)("div",{className:"spinner"})}),u=i.ZP.div.withConfig({componentId:"sc-86b17283-0"})(a(),e=>e.theme.loading,e=>e.theme.loading,e=>e.theme.loading)},21876:function(e){!function(){var t={675:function(e,t){"use strict";t.byteLength=function(e){var t=u(e),r=t[0],n=t[1];return(r+n)*3/4-n},t.toByteArray=function(e){var t,r,i=u(e),a=i[0],s=i[1],c=new o((a+s)*3/4-s),l=0,f=s>0?a-4:a;for(r=0;r>16&255,c[l++]=t>>8&255,c[l++]=255&t;return 2===s&&(t=n[e.charCodeAt(r)]<<2|n[e.charCodeAt(r+1)]>>4,c[l++]=255&t),1===s&&(t=n[e.charCodeAt(r)]<<10|n[e.charCodeAt(r+1)]<<4|n[e.charCodeAt(r+2)]>>2,c[l++]=t>>8&255,c[l++]=255&t),c},t.fromByteArray=function(e){for(var t,n=e.length,o=n%3,i=[],a=0,s=n-o;a>18&63]+r[o>>12&63]+r[o>>6&63]+r[63&o]);return i.join("")}(e,a,a+16383>s?s:a+16383));return 1===o?i.push(r[(t=e[n-1])>>2]+r[t<<4&63]+"=="):2===o&&i.push(r[(t=(e[n-2]<<8)+e[n-1])>>10]+r[t>>4&63]+r[t<<2&63]+"="),i.join("")};for(var r=[],n=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=0,s=i.length;a0)throw Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");-1===r&&(r=t);var n=r===t?0:4-r%4;return[r,n]}n["-".charCodeAt(0)]=62,n["_".charCodeAt(0)]=63},72:function(e,t,r){"use strict";/*! * The buffer module from node.js, for the browser. * * @author Feross Aboukhadijeh diff --git a/_next/static/chunks/pages/cs-3fbdfdd36b3aab17.js b/_next/static/chunks/pages/cs-3fbdfdd36b3aab17.js deleted file mode 100644 index 84ba9d3d..00000000 --- a/_next/static/chunks/pages/cs-3fbdfdd36b3aab17.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[677],{93408:function(n,e,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/cs",function(){return t(65266)}])},75756:function(n,e,t){"use strict";t.d(e,{w:function(){return s}});var c=t(85893),o=t(9008),i=t.n(o);let a=n=>"/images/".concat(n,".png"),s=n=>{let{content:e,backgroundImage:t}=n;return(0,c.jsxs)(i(),{children:[(0,c.jsx)("title",{children:"Rain Sugar BangBang Front-End"}),(0,c.jsx)("meta",{name:"description",content:"developer's blog"}),(0,c.jsx)("meta",{name:"viewport",content:"width=device-width, initial-scale=1"}),(0,c.jsx)("meta",{httpEquiv:"subject",content:e}),(0,c.jsx)("meta",{property:"og:title",content:"Rain Sugar BangBang"}),(0,c.jsx)("meta",{property:"og:description",content:"welcome to developer's blog"}),(0,c.jsx)("meta",{property:"og:image",content:"".concat(a("cards/".concat(t)))}),(0,c.jsx)("meta",{name:"naver-site-verification",content:"af9c8049662a7c6c85ea946e9eb561086e70062c"}),(0,c.jsx)("meta",{name:"google-site-verification",content:"Zgp7rA3RAdcRnhPPsGLK-t4KVTO86AejlDIQQIGpvLg"}),(0,c.jsx)("script",{async:!0,src:"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4077287316160146",crossOrigin:"anonymous"}),(0,c.jsx)("link",{rel:"icon",href:"/favicon.ico"})]})}},65266:function(n,e,t){"use strict";t.r(e),t.d(e,{__N_SSG:function(){return a},default:function(){return s}});var c=t(85893),o=t(71729),i=t(75756),a=!0;function s(n){let{posts:e}=n;return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(i.w,{content:"CS-ComputerScience",backgroundImage:"CS"}),(0,c.jsx)(o.Y,{posts:e})]})}}},function(n){n.O(0,[789,774,888,179],function(){return n(n.s=93408)}),_N_E=n.O()}]); \ No newline at end of file diff --git a/_next/static/chunks/pages/sw-9b1fdcf3f8a80910.js b/_next/static/chunks/pages/sw-9b1fdcf3f8a80910.js new file mode 100644 index 00000000..3e6967e3 --- /dev/null +++ b/_next/static/chunks/pages/sw-9b1fdcf3f8a80910.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[978],{28746:function(n,t,e){(window.__NEXT_P=window.__NEXT_P||[]).push(["/sw",function(){return e(56186)}])},75756:function(n,t,e){"use strict";e.d(t,{w:function(){return s}});var c=e(85893),o=e(9008),i=e.n(o);let a=n=>"/images/".concat(n,".png"),s=n=>{let{content:t,backgroundImage:e}=n;return(0,c.jsxs)(i(),{children:[(0,c.jsx)("title",{children:"Rain Sugar BangBang Front-End"}),(0,c.jsx)("meta",{name:"description",content:"developer's blog"}),(0,c.jsx)("meta",{name:"viewport",content:"width=device-width, initial-scale=1"}),(0,c.jsx)("meta",{httpEquiv:"subject",content:t}),(0,c.jsx)("meta",{property:"og:title",content:"Rain Sugar BangBang"}),(0,c.jsx)("meta",{property:"og:description",content:"welcome to developer's blog"}),(0,c.jsx)("meta",{property:"og:image",content:"".concat(a("cards/".concat(e)))}),(0,c.jsx)("meta",{name:"naver-site-verification",content:"af9c8049662a7c6c85ea946e9eb561086e70062c"}),(0,c.jsx)("meta",{name:"google-site-verification",content:"Zgp7rA3RAdcRnhPPsGLK-t4KVTO86AejlDIQQIGpvLg"}),(0,c.jsx)("script",{async:!0,src:"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4077287316160146",crossOrigin:"anonymous"}),(0,c.jsx)("link",{rel:"icon",href:"/favicon.ico"})]})}},56186:function(n,t,e){"use strict";e.r(t),e.d(t,{__N_SSG:function(){return a},default:function(){return s}});var c=e(85893),o=e(71729),i=e(75756),a=!0;function s(n){let{posts:t}=n;return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(i.w,{content:"정보처리기사",backgroundImage:"CS"}),(0,c.jsx)(o.Y,{posts:t})]})}}},function(n){n.O(0,[789,774,888,179],function(){return n(n.s=28746)}),_N_E=n.O()}]); \ No newline at end of file diff --git a/cs.html b/cs.html deleted file mode 100644 index 892259ed..00000000 --- a/cs.html +++ /dev/null @@ -1 +0,0 @@ -Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/css-a-tag-style-fix.html b/css-a-tag-style-fix.html index d11f1aa0..e53fc965 100644 --- a/css-a-tag-style-fix.html +++ b/css-a-tag-style-fix.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

Rain Sugar BangBang

css

a 태그(Link 태그) 기본 스타일 제거하기

2023-03-09

a태그 기본 스타일 제거와 스타일 적용

+Rain Sugar BangBang Front-End

Rain Sugar BangBang

css

a 태그(Link 태그) 기본 스타일 제거하기

2023-03-09

a태그 기본 스타일 제거와 스타일 적용

/* 기본 밑줄 제거 */
 a {
   text-decoration: none;
@@ -23,4 +23,4 @@
 /* 마우스로 링크를 클릭하고 뗄 때까지의 글자색을 정의 */
 a:active {
   color: green;
-}

"css" 카테고리의 다른 글

    COMMENTS

    \ No newline at end of file +}

    "css" 카테고리의 다른 글

      COMMENTS

      \ No newline at end of file diff --git a/css-no-selection.html b/css-no-selection.html index fbbbf4e4..09f578f0 100644 --- a/css-no-selection.html +++ b/css-no-selection.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

      Rain Sugar BangBang

      css

      CSS로 글자 더블클릭 시 선택자(파란색 마스킹) 없애기

      2023-03-09

      더블클릭시 파란색 선택자 생성 막는 방법

      +Rain Sugar BangBang Front-End

      Rain Sugar BangBang

      css

      CSS로 글자 더블클릭 시 선택자(파란색 마스킹) 없애기

      2023-03-09

      더블클릭시 파란색 선택자 생성 막는 방법

      div {
         user-select: none;
         -webkit-user-select: none;
      @@ -9,4 +9,4 @@
         /* konqueror browsers */
       
         -ms-user-select: none; /* IE10+ */
      -}

      "css" 카테고리의 다른 글

        COMMENTS

        \ No newline at end of file +}

        "css" 카테고리의 다른 글

          COMMENTS

          \ No newline at end of file diff --git a/css-use-download-font.html b/css-use-download-font.html index c777df53..554f4419 100644 --- a/css-use-download-font.html +++ b/css-use-download-font.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

          Rain Sugar BangBang

          css

          CSS - 배포 사이트에 다운로드 폰트 적용하기

          2023-03-31
          +Rain Sugar BangBang Front-End

          Rain Sugar BangBang

          css

          CSS - 배포 사이트에 다운로드 폰트 적용하기

          2023-03-31

          기본적으로 CSS에서 제공하는 폰트가 마음에 들지 않거나 내 프로젝트에 어울리지 않는 경우 간단하게 다운로드한 폰트를 프로젝트에 적용해볼 수 있다.

          1. 폰트 다운로드

          @@ -26,4 +26,4 @@

          4. CSS 사용

          평소에 폰트를 적용하는 것처럼 CSS를 적용해주면 된다.

          body {
             font-family: maplestory;
          -}

          "css" 카테고리의 다른 글

            COMMENTS

            \ No newline at end of file +}

            "css" 카테고리의 다른 글

              COMMENTS

              \ No newline at end of file diff --git a/css.html b/css.html index 3ff44299..058585c2 100644 --- a/css.html +++ b/css.html @@ -1 +1 @@ -Rain Sugar BangBang Front-End \ No newline at end of file +Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/github-git-blog-utterances-comment.html b/github-git-blog-utterances-comment.html index de063123..793971ae 100644 --- a/github-git-blog-utterances-comment.html +++ b/github-git-blog-utterances-comment.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

              Rain Sugar BangBang

              github

              utterances를 이용한 Nextjs Github 블로그 댓글 기능 구현

              2023-03-14

              utterances란?

              +Rain Sugar BangBang Front-End

              Rain Sugar BangBang

              github

              utterances를 이용한 Nextjs Github 블로그 댓글 기능 구현

              2023-03-14

              utterances란?

              Github repository의 이슈 기능을 활용해 내 웹사이트에 댓글 기능을 추가할 수 있는 도구로, 별도의 백엔드 구성을 하지 않고 댓글들을 관리할 수 있다는 장점이 있다.

              @@ -96,4 +96,4 @@

              5. script를 블로그 repo code에 복사

              return <section id="comment-box" />; };

              코드 수정 후 원하는대로 동작하는 모습을 볼 수 있었다.

              -

              image

              "Simple Memo" 카테고리의 다른 글

                COMMENTS

                \ No newline at end of file +

                image

                "Simple Memo" 카테고리의 다른 글

                  COMMENTS

                  \ No newline at end of file diff --git a/github-git-cmd.html b/github-git-cmd.html index dd5dd943..4beb3d90 100644 --- a/github-git-cmd.html +++ b/github-git-cmd.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                  Rain Sugar BangBang

                  github

                  깃 명령어 모음

                  2023-03-09

                  깃 명령어 모음

                  +Rain Sugar BangBang Front-End

                  Rain Sugar BangBang

                  github

                  깃 명령어 모음

                  2023-03-09

                  깃 명령어 모음

                  디렉토리

                  1. 홈 디렉토리로 이동
                  2. @@ -275,4 +275,4 @@

                    기타 명령어

                    $ git tag [태그이름][커밋 ID]
                     $ git tag Version_2 86a99 # tag 달기
                     $ git tag    #tag 조회하기
                    -$ git show Version_2

                  "Simple Memo" 카테고리의 다른 글

                    COMMENTS

                    \ No newline at end of file +$ git show Version_2

                    "Simple Memo" 카테고리의 다른 글

                      COMMENTS

                      \ No newline at end of file diff --git a/github-git-custom-blog.html b/github-git-custom-blog.html index ffc2bee8..00311767 100644 --- a/github-git-custom-blog.html +++ b/github-git-custom-blog.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                      Rain Sugar BangBang

                      github

                      Nextjs를 이용한 Github pages 블로그 만들기

                      2023-03-11

                      Github pages와 Nextjs를 선택한 이유

                      +Rain Sugar BangBang Front-End

                      Rain Sugar BangBang

                      github

                      Nextjs를 이용한 Github pages 블로그 만들기

                      2023-03-11

                      Github pages와 Nextjs를 선택한 이유

                      1. Github pages

                      vercel도 Nextjs로 쉽게 배포할 수 있지만, 백엔드는 별도로 만들지 않을 계획이었기에, 추후 댓글 기능이나 추천 글 목록 등을 Github 사이트 하나로 모두 관리하기 위해서 Github pages를 이용하기로 했다.

                      @@ -65,4 +65,4 @@

                      7. 배포 후 설정

                      터미널에 위와 같은 메세지가 뜨면, repository => setting => pages의 branch에 gh-pages가 생성돼있는 것을 볼 수 있음

                      gh-pages를 배포 브랜치로 설정해준 후 저장

                      image

                      -

                      설정을 완료하고 상단의 Visit site로 페이지를 접속하면 Nextjs 기본 화면이 잘 나오는 것을 볼 수 있다.

                      "Simple Memo" 카테고리의 다른 글

                        COMMENTS

                        \ No newline at end of file +

                        설정을 완료하고 상단의 Visit site로 페이지를 접속하면 Nextjs 기본 화면이 잘 나오는 것을 볼 수 있다.

                        "Simple Memo" 카테고리의 다른 글

                          COMMENTS

                          \ No newline at end of file diff --git a/github-git-use-markdown-viewer.html b/github-git-use-markdown-viewer.html index 6db94ac5..0d0619ce 100644 --- a/github-git-use-markdown-viewer.html +++ b/github-git-use-markdown-viewer.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                          Rain Sugar BangBang

                          github

                          Github pages 블로그 Markdown 불러오기

                          2023-03-12

                          Markdown을 통해 블로그를 작성하는 이유

                          +Rain Sugar BangBang Front-End

                          Rain Sugar BangBang

                          github

                          Github pages 블로그 Markdown 불러오기

                          2023-03-12

                          Markdown을 통해 블로그를 작성하는 이유

                          • 머리말을 따로 설정해 route 링크를 설정해주거나 제목, 날짜, 카테고리, 등을 설정하여 블로그 탐색을 용이하게 해줄 수 있다.
                          • Markdown을 repository에 저장함으로써 githubAPI 등을 이용한 데이터 조회를 가능하게 한다.
                          • @@ -128,4 +128,4 @@

                            3. Markdown 파일 생성

                            파일 생성 후 해당 링크 접속 시 정상적으로 접속이 가능해진다.

                            ex

                            localhost:3000/first-post

                            -

                            url은 [slug].tsx 파일이 위치한 경로에 따라 달라질 수 있다. (nextjs 라우팅 방식)

                          "Simple Memo" 카테고리의 다른 글

                            COMMENTS

                            \ No newline at end of file +

                            url은 [slug].tsx 파일이 위치한 경로에 따라 달라질 수 있다. (nextjs 라우팅 방식)

                            "Simple Memo" 카테고리의 다른 글

                              COMMENTS

                              \ No newline at end of file diff --git a/github-github-api-get-repo-data.html b/github-github-api-get-repo-data.html index 68116c92..4158b953 100644 --- a/github-github-api-get-repo-data.html +++ b/github-github-api-get-repo-data.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                              Rain Sugar BangBang

                              github

                              GithubAPI를 이용한 repository 내 Markdown 파일 정보 불러오기

                              2023-03-13

                              Github REST API 설명서

                              +Rain Sugar BangBang Front-End

                              Rain Sugar BangBang

                              github

                              GithubAPI를 이용한 repository 내 Markdown 파일 정보 불러오기

                              2023-03-13

                              Github REST API 설명서

                              https://docs.github.com/ko/rest?apiVersion=2022-11-28

                              GithubAPI 이용 시 토큰이 필요한 경우 아래와 같이 토큰 발급도 가능하다.

                                @@ -66,4 +66,4 @@

                                3. 데이터 가공

                                };

                                상수 frontmatter는 머리말을 가져오지만 예쁘게 가져오진 않는다.

                                image

                                -

                                때문에 if(frontmatter){...}와 같이 필요한 데이터를 정규식이나 string메서드를 통해 원하는대로 가공해 사용하여 사용해야 한다.

                              "Simple Memo" 카테고리의 다른 글

                                COMMENTS

                                \ No newline at end of file +

                                때문에 if(frontmatter){...}와 같이 필요한 데이터를 정규식이나 string메서드를 통해 원하는대로 가공해 사용하여 사용해야 한다.

                                "Simple Memo" 카테고리의 다른 글

                                  COMMENTS

                                  \ No newline at end of file diff --git a/github-markdown-syntex.html b/github-markdown-syntex.html index 4392fc40..5f6d8d95 100644 --- a/github-markdown-syntex.html +++ b/github-markdown-syntex.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                  Rain Sugar BangBang

                                  github

                                  Markdown 문법 정리

                                  2023-03-31

                                  1. headers

                                  +Rain Sugar BangBang Front-End

                                  Rain Sugar BangBang

                                  \ No newline at end of file +

                                  참조 https://inpa.tistory.com/entry/MarkDown-%F0%9F%93%9A-%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4-%EB%AC%B8%EB%B2%95-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC

                                  "Simple Memo" 카테고리의 다른 글

                                    COMMENTS

                                    \ No newline at end of file diff --git a/github-token-expired.html b/github-token-expired.html index 50100064..98956ebb 100644 --- a/github-token-expired.html +++ b/github-token-expired.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                    Rain Sugar BangBang

                                    github

                                    Github Token을 이용한 GithubAPI 사용

                                    2023-03-31

                                    본 포스팅은 NextJS 13 환경에서 진행한 내용을 바탕으로 한다. GithubAPI 사용법은 공식 문서에 상세히 나와있기 때문에 사용법보다 토큰 만료 문제를 주로 다룬다.

                                    +Rain Sugar BangBang Front-End

                                    Rain Sugar BangBang

                                    github

                                    Github Token을 이용한 GithubAPI 사용

                                    2023-03-31

                                    본 포스팅은 NextJS 13 환경에서 진행한 내용을 바탕으로 한다. GithubAPI 사용법은 공식 문서에 상세히 나와있기 때문에 사용법보다 토큰 만료 문제를 주로 다룬다.

                                    GithubAPI란?

                                    Github의 기능을 REST API 형식으로 사용할 수 있도록 도와주는 기능이다.

                                    @@ -59,4 +59,4 @@

                                    2. auth Token 설정

                                    하지만 .env에 토큰을 설정하고 commit을 하게되면 gitignore를 설정해줘도 github가 자동으로 감지해 토큰을 만료시키기 때문에 .env.local서 테스트하고 .env의 토큰은 지운 뒤에 사용해주자.

                                    image


                                    -

                                    참조 : https://docs.github.com/en

                                    "Simple Memo" 카테고리의 다른 글

                                      COMMENTS

                                      \ No newline at end of file +

                                      참조 : https://docs.github.com/en

                                      "Simple Memo" 카테고리의 다른 글

                                        COMMENTS

                                        \ No newline at end of file diff --git a/github.html b/github.html index f367d848..1054b389 100644 --- a/github.html +++ b/github.html @@ -1 +1 @@ -Rain Sugar BangBang Front-End \ No newline at end of file +Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/html-input-basic.html b/html-input-basic.html index 557cd61b..781fbee8 100644 --- a/html-input-basic.html +++ b/html-input-basic.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                        Rain Sugar BangBang

                                        html

                                        HTML input 속성 이용하기

                                        2023-02-09

                                        Input element 사용하기

                                        +Rain Sugar BangBang Front-End

                                        Rain Sugar BangBang

                                        html

                                        HTML input 속성 이용하기

                                        2023-02-09

                                        Input element 사용하기

                                        타입

                                        • type="text" 일반 텍스트
                                        • @@ -22,4 +22,4 @@

                                          속성

                                        • autocomplete="off" "브라우저의 자동완성 제거"
                                        • checked "radio나 checkbox 등의 체크 여부"
                                        • name input의 이름으로 radio나 checkbox의 카테고리 묶음이나 javascript 이벤트 등을 유연하게 적용할 수 있음
                                        • -

                                        "html" 카테고리의 다른 글

                                          COMMENTS

                                          \ No newline at end of file +

                                          "html" 카테고리의 다른 글

                                            COMMENTS

                                            \ No newline at end of file diff --git a/html-meta-tag.html b/html-meta-tag.html index 07b46434..fb2f16f6 100644 --- a/html-meta-tag.html +++ b/html-meta-tag.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                            Rain Sugar BangBang

                                            html

                                            SEO(검색엔진 최적화)를 위한 HTML의 meta tag 간단 정리

                                            2023-03-09

                                            메타태그

                                            +Rain Sugar BangBang Front-End

                                            Rain Sugar BangBang

                                            html

                                            SEO(검색엔진 최적화)를 위한 HTML의 meta tag 간단 정리

                                            2023-03-09

                                            메타태그

                                            메타태그란 쉽게 말해서 눈에 보이지 않는 문서가 가지고 있는 정보들을 담을 수 있는 태그를 말한다.

                                            1. 주로 사용해볼 수 있는 요소

                                            검색 엔진에 검색되는 단어 지정

                                            @@ -91,4 +91,4 @@

                                            4. 써볼만한 태그

                                          • Random bars horizontal : 수평선이 무작위로 생성
                                          • Random bars vertical : 수직선이 무작위로 생성
                                          • Random : 임의로 생성
                                          • -

                                            "html" 카테고리의 다른 글

                                              COMMENTS

                                              \ No newline at end of file +

                                              "html" 카테고리의 다른 글

                                                COMMENTS

                                                \ No newline at end of file diff --git a/html.html b/html.html index 4ebaf1d1..43c3c319 100644 --- a/html.html +++ b/html.html @@ -1 +1 @@ -Rain Sugar BangBang Front-End \ No newline at end of file +Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/index.html b/index.html index b59ba85c..420b70f0 100644 --- a/index.html +++ b/index.html @@ -1 +1 @@ -Rain Sugar BangBang Front-End \ No newline at end of file +Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/javascript-basic-1-num-str.html b/javascript-basic-1-num-str.html index 21a3fc83..c2d19aed 100644 --- a/javascript-basic-1-num-str.html +++ b/javascript-basic-1-num-str.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                Rain Sugar BangBang

                                                javascript

                                                javascript 기초 1 숫자열문자열

                                                2023-03-06

                                                1. 숫자열을 문자열로 바꾸기

                                                +Rain Sugar BangBang Front-End

                                                Rain Sugar BangBang

                                                javascript

                                                javascript 기초 1 숫자열문자열

                                                2023-03-06

                                                1. 숫자열을 문자열로 바꾸기

                                                
                                                 // 숫자는 숫자+""만 해줘도 (혹은 다른 문자를 더해줘도) 문자열로 바뀐다.
                                                 
                                                @@ -47,4 +47,4 @@ 

                                                3. 문자열 다루기

                                                .includes() - 문자열이 특정 문자열을 포함하는지 확인 .charAt(0) - 스트링의 0번째 글자 가져오기(result = string) .substring(0,5) - 스트링의 0~4번째 글자 가져오기 -

                                                "javascript" 카테고리의 다른 글

                                                  COMMENTS

                                                  \ No newline at end of file +

                                                  "javascript" 카테고리의 다른 글

                                                    COMMENTS

                                                    \ No newline at end of file diff --git a/javascript-basic-2-date.html b/javascript-basic-2-date.html index 1c9053b9..3a732033 100644 --- a/javascript-basic-2-date.html +++ b/javascript-basic-2-date.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                    Rain Sugar BangBang

                                                    javascript

                                                    javascript 기초 2 날짜다루기

                                                    2023-03-06

                                                    Javascript 날짜 다루기

                                                    +Rain Sugar BangBang Front-End

                                                    Rain Sugar BangBang

                                                    javascript

                                                    javascript 기초 2 날짜다루기

                                                    2023-03-06

                                                    Javascript 날짜 다루기

                                                    날짜 계산

                                                    // 날짜 예제
                                                     // Sunday - Saturday : 0 - 6
                                                    @@ -27,4 +27,4 @@ 

                                                    날짜 계산

                                                    console.log(minutes); // 현재 분 추출 let seconds = today?.getSeconds(); -console.log(seconds); // 현재 초 추출

                                                    "javascript" 카테고리의 다른 글

                                                      COMMENTS

                                                      \ No newline at end of file +console.log(seconds); // 현재 초 추출

                                                      "javascript" 카테고리의 다른 글

                                                        COMMENTS

                                                        \ No newline at end of file diff --git a/javascript-basic-3-math.html b/javascript-basic-3-math.html index 9d0debe3..0ee87437 100644 --- a/javascript-basic-3-math.html +++ b/javascript-basic-3-math.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                        Rain Sugar BangBang

                                                        javascript

                                                        javascript 기초 3 Math 다뤄보기

                                                        2023-03-06

                                                        Javascript Math 메서드

                                                        +Rain Sugar BangBang Front-End

                                                        Rain Sugar BangBang

                                                        javascript

                                                        javascript 기초 3 Math 다뤄보기

                                                        2023-03-06

                                                        Javascript Math 메서드

                                                        1. 소수점 표현

                                                        // n = Number
                                                         Math.ceil(n); // 올림
                                                        @@ -23,4 +23,4 @@ 

                                                        2. 계산/정수 처리

                                                        Math.min(); // 인자 중 가장 작은 값 반환 - Math.max 반대 Math.pow(x, y); // 인자간 거듭제곱 값 반환 === x**y와 동일 Math.random(); // 0이상 1미만 랜덤값 반환 -Math.sqrt(); // 제곱근 반환

                                                        "javascript" 카테고리의 다른 글

                                                          COMMENTS

                                                          \ No newline at end of file +Math.sqrt(); // 제곱근 반환

                                                          "javascript" 카테고리의 다른 글

                                                            COMMENTS

                                                            \ No newline at end of file diff --git a/javascript-basic-4-array-1.html b/javascript-basic-4-array-1.html index ef6f6a9d..8afe0391 100644 --- a/javascript-basic-4-array-1.html +++ b/javascript-basic-4-array-1.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                            Rain Sugar BangBang

                                                            javascript

                                                            javascript 기초 4 배열 다루기 - 1

                                                            2023-03-06

                                                            JavaScript 배열 다뤄보기

                                                            +Rain Sugar BangBang Front-End

                                                            Rain Sugar BangBang

                                                            javascript

                                                            javascript 기초 4 배열 다루기 - 1

                                                            2023-03-06

                                                            JavaScript 배열 다뤄보기

                                                            1. 배열 수정

                                                            let Arr = [1, 2, 3, 4, 5];
                                                             
                                                            @@ -72,4 +72,4 @@ 

                                                            3. 배열 메서드

                                                            // 매개변수가 x밖에 없다면 x 이상 index에 해당하는 배열 추출 // 매개변수가 음수인 경우 ex. (-2) 라면, 배열 끝에서 2번째 index 이상의 배열 추출 .slice(0, 2) // [1,2] -

                                                            "javascript" 카테고리의 다른 글

                                                              COMMENTS

                                                              \ No newline at end of file +

                                                              "javascript" 카테고리의 다른 글

                                                                COMMENTS

                                                                \ No newline at end of file diff --git a/javascript-basic-4-array-2.html b/javascript-basic-4-array-2.html index 26f25207..dff542fe 100644 --- a/javascript-basic-4-array-2.html +++ b/javascript-basic-4-array-2.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                Rain Sugar BangBang

                                                                javascript

                                                                javascript 기초 4 배열 다루기 - 2

                                                                2023-03-06

                                                                고차함수와 메서드를 통해 JavaScript 배열 다뤄보기

                                                                +Rain Sugar BangBang Front-End

                                                                Rain Sugar BangBang

                                                                javascript

                                                                javascript 기초 4 배열 다루기 - 2

                                                                2023-03-06

                                                                고차함수와 메서드를 통해 JavaScript 배열 다뤄보기

                                                                고차함수란?

                                                                함수를 인자로 받거나, 함수를 반환함으로써 작동하는 함수로, .map() , .filter() , .forEach() , .reduce() 등이 있다.

                                                                @@ -60,4 +60,4 @@

                                                                indexOf()

                                                                const indexOfFirst2 = comment.indexOf(searchStr2); console.log(indexOfFirst); // 4 // 오류는 4번째 인덱스부터 시작한다. -console.log(indexOfFirst2); // 17 // 해주세요는 17번째 인덱스부터 시작한다.

                                                                "javascript" 카테고리의 다른 글

                                                                  COMMENTS

                                                                  \ No newline at end of file +console.log(indexOfFirst2); // 17 // 해주세요는 17번째 인덱스부터 시작한다.

                                                                  "javascript" 카테고리의 다른 글

                                                                    COMMENTS

                                                                    \ No newline at end of file diff --git a/javascript-basic-5-loop.html b/javascript-basic-5-loop.html index 91429398..f28728e0 100644 --- a/javascript-basic-5-loop.html +++ b/javascript-basic-5-loop.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                    Rain Sugar BangBang

                                                                    javascript

                                                                    javascript 기초 5 반복문 다루기

                                                                    2023-03-06

                                                                    for문 사용법

                                                                    +Rain Sugar BangBang Front-End

                                                                    Rain Sugar BangBang

                                                                    javascript

                                                                    javascript 기초 5 반복문 다루기

                                                                    2023-03-06

                                                                    for문 사용법

                                                                    for문

                                                                    
                                                                     const arr = [1,2,3,4,5,6,7,8,9,10];
                                                                    @@ -102,4 +102,4 @@ 

                                                                    .map()

                                                                    "80(7번째)", "90(8번째)", "100(9번째)") -];

                                                                    "javascript" 카테고리의 다른 글

                                                                      COMMENTS

                                                                      \ No newline at end of file +];

                                                                      "javascript" 카테고리의 다른 글

                                                                        COMMENTS

                                                                        \ No newline at end of file diff --git a/javascript-dynamic-import.html b/javascript-dynamic-import.html index f2ad8414..6b733e07 100644 --- a/javascript-dynamic-import.html +++ b/javascript-dynamic-import.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                        Rain Sugar BangBang

                                                                        javascript

                                                                        Dynamic import를 사용해 동적으로 모듈 가져오기

                                                                        2023-03-18

                                                                        Dynamic import란?

                                                                        +Rain Sugar BangBang Front-End

                                                                        Rain Sugar BangBang

                                                                        javascript

                                                                        Dynamic import를 사용해 동적으로 모듈 가져오기

                                                                        2023-03-18

                                                                        Dynamic import란?

                                                                        import() 표현식으로 사용하며, 표현식은 모듈을 읽고 해당 모듈이 내보내는 것들을 모두 포함하는 객체를 담은 이행된 Promise를 반환한다. 호출은 어디서나 가능하다.

                                                                        @@ -81,4 +81,4 @@

                                                                        주의사항 : import()는 함수 호출과 문법이 유사해 보이지만

                                                                        참조

                                                                        MDN : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import

                                                                        -

                                                                        Modern js : https://ko.javascript.info/modules-dynamic-imports

                                                                        "javascript" 카테고리의 다른 글

                                                                          COMMENTS

                                                                          \ No newline at end of file +

                                                                          Modern js : https://ko.javascript.info/modules-dynamic-imports

                                                                          "javascript" 카테고리의 다른 글

                                                                            COMMENTS

                                                                            \ No newline at end of file diff --git a/javascript-how-to-use-promise.all.html b/javascript-how-to-use-promise.all.html index f5354165..e5d78942 100644 --- a/javascript-how-to-use-promise.all.html +++ b/javascript-how-to-use-promise.all.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                            Rain Sugar BangBang

                                                                            javascript

                                                                            Promise.all()은 언제 쓰고 왜 쓰나요? - 여러 비동기 요청 한 번에 처리하기

                                                                            2023-05-19

                                                                            javascript를 통해 비동기 코드를 처리하는 기본적인 방법으로는 Callback 함수, Promise, async/await 등이 있다. 하지만 다수의 비동기 요청의 실행이 필요한 경우 Promise.all()을 사용해볼 수 있다.

                                                                            +Rain Sugar BangBang Front-End

                                                                            Rain Sugar BangBang

                                                                            javascript

                                                                            Promise.all()은 언제 쓰고 왜 쓰나요? - 여러 비동기 요청 한 번에 처리하기

                                                                            2023-05-19

                                                                            javascript를 통해 비동기 코드를 처리하는 기본적인 방법으로는 Callback 함수, Promise, async/await 등이 있다. 하지만 다수의 비동기 요청의 실행이 필요한 경우 Promise.all()을 사용해볼 수 있다.

                                                                            Promise.all()이란?

                                                                            여러 개의 Promise를 동시에 처리하고, 모든 Promise가 완료되었을 때 그 결과들을 반환하는 javascript의 메서드이다.

                                                                            Promise.all()은 배열형태의 Promise들을 인자로 받고, 이 Promise들은 병렬적으로 실행된다. Promise.all()은 해당 Promise들의 결과를 배열로 반환한다.

                                                                            @@ -65,4 +65,4 @@

                                                                            2. Promise.all()


                                                                            참조

                                                                            https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

                                                                            -

                                                                            https://code-masterjung.tistory.com/91

                                                                            "javascript" 카테고리의 다른 글

                                                                              COMMENTS

                                                                              \ No newline at end of file +

                                                                              https://code-masterjung.tistory.com/91

                                                                              "javascript" 카테고리의 다른 글

                                                                                COMMENTS

                                                                                \ No newline at end of file diff --git a/javascript-jest-test-code.html b/javascript-jest-test-code.html index af58fc96..1cbe3cfe 100644 --- a/javascript-jest-test-code.html +++ b/javascript-jest-test-code.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                Rain Sugar BangBang

                                                                                javascript

                                                                                jest를 통한 테스트코드 사용

                                                                                2023-03-18

                                                                                jest란?

                                                                                +Rain Sugar BangBang Front-End

                                                                                Rain Sugar BangBang

                                                                                javascript

                                                                                jest를 통한 테스트코드 사용

                                                                                2023-03-18

                                                                                jest란?

                                                                                단순성에 중점을 둔 javascript 테스트 프레임워크로, babel, Typescript, node, React, Angular, Vue 등을 사용하는 프로젝트에서 작동한다.

                                                                                @@ -91,4 +91,4 @@

                                                                                3. 로직을 개선하며 1~3을 반복한다.

                                                                                TDD의 예시

                                                                                https://velog.io/@velopert/TDD%EC%9D%98-%EC%86%8C%EA%B0%9C


                                                                                -

                                                                                참조 : https://jestjs.io/

                                                                                "javascript" 카테고리의 다른 글

                                                                                  COMMENTS

                                                                                  \ No newline at end of file +

                                                                                  참조 : https://jestjs.io/

                                                                                  "javascript" 카테고리의 다른 글

                                                                                    COMMENTS

                                                                                    \ No newline at end of file diff --git a/javascript.html b/javascript.html index 45283da9..b98349a3 100644 --- a/javascript.html +++ b/javascript.html @@ -1 +1 @@ -Rain Sugar BangBang Front-End \ No newline at end of file +Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/nestjs-custom-repository.html b/nestjs-custom-repository.html index 25dafdb1..9abc9043 100644 --- a/nestjs-custom-repository.html +++ b/nestjs-custom-repository.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                    Rain Sugar BangBang

                                                                                    nestjs

                                                                                    Entityrepository()를 대신한 Repository 생성

                                                                                    2023-03-04

                                                                                    포스팅 시점 nestjs 버전 : 9.x.x

                                                                                    +Rain Sugar BangBang Front-End

                                                                                    Rain Sugar BangBang

                                                                                    nestjs

                                                                                    Entityrepository()를 대신한 Repository 생성

                                                                                    2023-03-04

                                                                                    포스팅 시점 nestjs 버전 : 9.x.x

                                                                                    새로운 토이 프로젝트를 만들면서 백엔드를 구축할 프레임워크로 nestjs를 이용하기로 마음먹었다.

                                                                                    원래 사용하고 있던 javascript/typescript와 동일한 언어를 사용하기 때문에 백엔드 구축에 비교적 진입장벽이 낮다고 생각했기 때문에다.

                                                                                    nest 작동순서

                                                                                    @@ -222,4 +222,4 @@

                                                                                    Module에 import 후 service 비즈니스 로직에만 repository를 넣어 정상적으로 테이블에 있는 데이터가 추출되는 것이 잘 확인된다.

                                                                                    참고 :

                                                                                    https://orkhan.gitbook.io/typeorm/readme_ko

                                                                                    -

                                                                                    https://docs.nestjs.com/techniques/database

                                                                                    "nestjs" 카테고리의 다른 글

                                                                                      COMMENTS

                                                                                      \ No newline at end of file +

                                                                                      https://docs.nestjs.com/techniques/database

                                                                                      "nestjs" 카테고리의 다른 글

                                                                                        COMMENTS

                                                                                        \ No newline at end of file diff --git a/nestjs-localhost-https.html b/nestjs-localhost-https.html index 9a647da8..97232e91 100644 --- a/nestjs-localhost-https.html +++ b/nestjs-localhost-https.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                        Rain Sugar BangBang

                                                                                        nestjs

                                                                                        Localhost 환경에서 HTTPS 적용하기

                                                                                        2023-04-30

                                                                                        Local 환경에서 https 설정이 필요한 이유

                                                                                        +Rain Sugar BangBang Front-End

                                                                                        Rain Sugar BangBang

                                                                                        nestjs

                                                                                        Localhost 환경에서 HTTPS 적용하기

                                                                                        2023-04-30

                                                                                        Local 환경에서 https 설정이 필요한 이유

                                                                                        로컬 환경에서 내가 원하는대로 기능들이 작동한다고 해도, 배포 환경과 로컬 개발 환경의 차이로 인해 코드의 수정이 필요해질 수 있다. 이러한 이유로 개발 환경을 배포 환경과 최대한 동일하게 만들어주는 것이 좋다. 또한 최근의 배포 환경들은 대부분 https로 이뤄져 있기 때문에 그에 맞춰 설정해줄 필요가 있다.

                                                                                        자체 서명 인증서 생성 (openssl 사용)

                                                                                        # 개인 키 생성
                                                                                        @@ -37,4 +37,4 @@ 

                                                                                        HTTPS 구성 설정 - main.ts

                                                                                        그럼에도 오류가 발생하는 경우 아래의 링크를 통해 크롬 환경설정을 해주면 된다.

                                                                                        chrome://flags/#allow-insecure-localhost

                                                                                        유효하지 않은 인증서 허용으로 설정

                                                                                        -

                                                                                        image

                                                                                        "nestjs" 카테고리의 다른 글

                                                                                          COMMENTS

                                                                                          \ No newline at end of file +

                                                                                          image

                                                                                          "nestjs" 카테고리의 다른 글

                                                                                            COMMENTS

                                                                                            \ No newline at end of file diff --git a/nestjs-server-client-cookie.html b/nestjs-server-client-cookie.html index 2ae33a65..74157ac1 100644 --- a/nestjs-server-client-cookie.html +++ b/nestjs-server-client-cookie.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                            Rain Sugar BangBang

                                                                                            nestjs

                                                                                            NestJS에서 클라이언트로 쿠키 보내기 (왜 내 쿠키는 안받아줘요?)

                                                                                            2023-04-13

                                                                                            image

                                                                                            +Rain Sugar BangBang Front-End

                                                                                            Rain Sugar BangBang

                                                                                            nestjs

                                                                                            NestJS에서 클라이언트로 쿠키 보내기 (왜 내 쿠키는 안받아줘요?)

                                                                                            2023-04-13

                                                                                            image

                                                                                            쿠키(Cookie)란?

                                                                                            쿠키는 서버에서 클라이언트에게 보내는 작은 데이터 조각이다. 일반적으로 브라우저에서 웹 사이트를 방문할 때 쿠키를 사용하여 사용자의 활동을 기록하고, 이후에 그 사용자가 같은 웹 사이트를 방문할 때 이 정보를 사용해 그에 맞게 동작하게 해준다. 하지만 쿠키는 당사자뿐만 아닌 제 3자가 조회하는 것도 가능하기 때문에 개인 정보를 담는 등 보안상 민감한 정보를 저장하는 데에는 적합하지 않기 때문에 탈취되거나 사용자에 의해 조작되어도 크게 문제 되지 않을 정보를 주로 저장한다. (ex. 다크 모드, 장바구니 목록 등)

                                                                                            서버에서 클라이언트로 Cookie 전송 예제

                                                                                            @@ -106,4 +106,4 @@

                                                                                            Localhost 환 }

                                                                                            클라이언트 단에서는 withCredentials:true 옵션 설정 외에는 특별히 해줄 것이 없다.


                                                                                            -

                                                                                            참조 : https://docs.nestjs.com/techniques/cookies

                                                                                            "nestjs" 카테고리의 다른 글

                                                                                              COMMENTS

                                                                                              \ No newline at end of file +

                                                                                              참조 : https://docs.nestjs.com/techniques/cookies

                                                                                              "nestjs" 카테고리의 다른 글

                                                                                                COMMENTS

                                                                                                \ No newline at end of file diff --git a/nestjs-server-client-cookie2.html b/nestjs-server-client-cookie2.html index 05a82123..7f32c958 100644 --- a/nestjs-server-client-cookie2.html +++ b/nestjs-server-client-cookie2.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                Rain Sugar BangBang

                                                                                                nestjs

                                                                                                NestJS에서 클라이언트로 쿠키 보내기 (왜 내 쿠키는 안받아줘요?) - 배포환경

                                                                                                2023-04-30

                                                                                                서버에서 브라우저로 쿠키 저장

                                                                                                +Rain Sugar BangBang Front-End

                                                                                                Rain Sugar BangBang

                                                                                                nestjs

                                                                                                NestJS에서 클라이언트로 쿠키 보내기 (왜 내 쿠키는 안받아줘요?) - 배포환경

                                                                                                2023-04-30

                                                                                                서버에서 브라우저로 쿠키 저장

                                                                                                분명 로컬에서는 쿠키가 정상적으로 브라우저에 저장되는 것을 볼 수 있었는데, 배포를 하고 나니 또다시 쿠키가 보이지 않았다. response headers에는 쿠키 값과 함께 다음과 같은 에러 문구를 볼 수 있었다.

                                                                                                this attempt to set a cookie via a set cookie header was blocked because its domain attribue was invalid with reqards to the current host url

                                                                                                @@ -29,4 +29,4 @@ httpOnly: true, domain: process.env.COOKIE_PARSE_DOMAIN, // ".product.com" }); -

                                                                                                같은 도메인을 사용하고 나서야 쿠키가 정상적으로 브라우저로 접속하는 것을 확인할 수 있었다.

                                                                                                "nestjs" 카테고리의 다른 글

                                                                                                  COMMENTS

                                                                                                  \ No newline at end of file +

                                                                                                  같은 도메인을 사용하고 나서야 쿠키가 정상적으로 브라우저로 접속하는 것을 확인할 수 있었다.

                                                                                                  "nestjs" 카테고리의 다른 글

                                                                                                    COMMENTS

                                                                                                    \ No newline at end of file diff --git a/nestjs-use-bcrypt.html b/nestjs-use-bcrypt.html index 97ec660e..81977c19 100644 --- a/nestjs-use-bcrypt.html +++ b/nestjs-use-bcrypt.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                    Rain Sugar BangBang

                                                                                                    nestjs

                                                                                                    Nestjs에서 bcrypt 사용하기

                                                                                                    2023-04-13

                                                                                                    bycrypt란?

                                                                                                    +Rain Sugar BangBang Front-End

                                                                                                    Rain Sugar BangBang

                                                                                                    nestjs

                                                                                                    Nestjs에서 bcrypt 사용하기

                                                                                                    2023-04-13

                                                                                                    bycrypt란?

                                                                                                    비밀번호 등을 안전하게 암호화하여 저장하고 검증할 수 있도록 도와주는 라이브러리로, 랜덤한 salt를 생성하고 이를 비밀번호와 함께 암호화하여 저장한다. 이후 비밀번호 검증 시에도 같은 salt를 사용하여 입력받은 비밀번호를 암호화한 후, 저장된 암호화된 비밀번호와 비교하여 일치하는지 검증한다.

                                                                                                    bcrypt는 암호화 강도를 선택할 수 있는 기능을 제공한다. 기본값은 10으로, 숫자가 클수록 강도가 높아지며 암호화에 소요되는 시간도 늘어난다. 보안을 위해 기본값 이상의 값을 권장한다.

                                                                                                    사용법

                                                                                                    @@ -25,4 +25,4 @@

                                                                                                    사용법

                                                                                                    if(match) // 인증 후 로직 }
                                                                                                    -

                                                                                                    참조 : https://github.com/kelektiv/node.bcrypt.js#readme

                                                                                                    "nestjs" 카테고리의 다른 글

                                                                                                      COMMENTS

                                                                                                      \ No newline at end of file +

                                                                                                      참조 : https://github.com/kelektiv/node.bcrypt.js#readme

                                                                                                      "nestjs" 카테고리의 다른 글

                                                                                                        COMMENTS

                                                                                                        \ No newline at end of file diff --git a/nestjs.html b/nestjs.html index aa439431..685181e6 100644 --- a/nestjs.html +++ b/nestjs.html @@ -1 +1 @@ -Rain Sugar BangBang Front-End \ No newline at end of file +Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/nextjs-link-userouter.html b/nextjs-link-userouter.html index 60570c3e..a9c16ea7 100644 --- a/nextjs-link-userouter.html +++ b/nextjs-link-userouter.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                        Rain Sugar BangBang

                                                                                                        nextjs

                                                                                                        Nextjs Link와 useRouter의 차이

                                                                                                        2023-03-18

                                                                                                        Link / useRouter 사용법

                                                                                                        +Rain Sugar BangBang Front-End

                                                                                                        Rain Sugar BangBang

                                                                                                        \ No newline at end of file +

                                                                                                        https://nextjs.org/docs/api-reference/next/router

                                                                                                        "nextjs" 카테고리의 다른 글

                                                                                                          COMMENTS

                                                                                                          \ No newline at end of file diff --git a/nextjs-loading.html b/nextjs-loading.html index 7e89c669..38103310 100644 --- a/nextjs-loading.html +++ b/nextjs-loading.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                          Rain Sugar BangBang

                                                                                                          nextjs

                                                                                                          nextjs에서의 로딩처리

                                                                                                          2023-03-04

                                                                                                          Nextjs에서 로딩처리하기

                                                                                                          +Rain Sugar BangBang Front-End

                                                                                                          Rain Sugar BangBang

                                                                                                          nextjs

                                                                                                          nextjs에서의 로딩처리

                                                                                                          2023-03-04

                                                                                                          Nextjs에서 로딩처리하기

                                                                                                          모든 페이지를 미리 렌더링하는 NextJS 특성상, 다른 페이지로 라우팅이 진행될때, 사용자는 가만히 멈춰있는 화면을 보게될 수 있다. 때문에 페이지 전환을 정지화면으로 두지 않기 위해 로딩 처리를 구현해볼 수 있다.

                                                                                                          로딩 스피너 세팅

                                                                                                          // _app.tsx
                                                                                                          @@ -53,4 +53,4 @@ 

                                                                                                          라우팅 시 적용될 이벤트 설정

                                                                                                          _app.tsx(jsx)에 로딩 스피너나 로딩 페이지를 적용해 놓으면,

                                                                                                          라우팅으로 인한 페이지 이동이 일어날 때마다 원하는 로딩 창을 호출해줄 수 있다.

                                                                                                          적용된 페이지

                                                                                                          -

                                                                                                          image

                                                                                                          "nextjs" 카테고리의 다른 글

                                                                                                            COMMENTS

                                                                                                            \ No newline at end of file +

                                                                                                            image

                                                                                                            "nextjs" 카테고리의 다른 글

                                                                                                              COMMENTS

                                                                                                              \ No newline at end of file diff --git a/nextjs-marked-webpack-imported-module-7-default.html b/nextjs-marked-webpack-imported-module-7-default.html index 523f609a..2ecea920 100644 --- a/nextjs-marked-webpack-imported-module-7-default.html +++ b/nextjs-marked-webpack-imported-module-7-default.html @@ -1,6 +1,6 @@ -Rain Sugar BangBang Front-End

                                                                                                              Rain Sugar BangBang

                                                                                                              nextjs

                                                                                                              WEBPACK_IMPORTED_MODULE_7_default is not a function 에러 해결

                                                                                                              2023-03-18

                                                                                                              에러 해결 방법

                                                                                                              +Rain Sugar BangBang Front-End

                                                                                                              Rain Sugar BangBang

                                                                                                              nextjs

                                                                                                              WEBPACK_IMPORTED_MODULE_7_default is not a function 에러 해결

                                                                                                              2023-03-18

                                                                                                              에러 해결 방법

                                                                                                              나의 경우 SSR 데이터를 contextAPI를 통해 패치하려 했으나, 정상적으로 받아오지 못할때 발생한 오류였다.

                                                                                                              로컬 실행 종료 후 재실행하니 정상적으로 데이터를 받아올 수 있었다.

                                                                                                              다소 허탈한 해결방안이었지만, SSR을 다루는 페이지의 데이터가 정상적으로 받아와지지 않는 경우, 코드 작성 후 프로그램을 다시 실행해보자.


                                                                                                              -

                                                                                                              참조 : https://github.com/vercel/next.js/issues/18090

                                                                                                              "nextjs" 카테고리의 다른 글

                                                                                                                COMMENTS

                                                                                                                \ No newline at end of file +

                                                                                                                참조 : https://github.com/vercel/next.js/issues/18090

                                                                                                                "nextjs" 카테고리의 다른 글

                                                                                                                  COMMENTS

                                                                                                                  \ No newline at end of file diff --git a/nextjs-pages-api-dir-unsupported.html b/nextjs-pages-api-dir-unsupported.html index 3311c13b..6dc0861b 100644 --- a/nextjs-pages-api-dir-unsupported.html +++ b/nextjs-pages-api-dir-unsupported.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                  Rain Sugar BangBang

                                                                                                                  nextjs

                                                                                                                  NextJS에서 API 디렉토리가 사용되지 않는 이유

                                                                                                                  2023-04-30

                                                                                                                  NextJS는 pages/api 디렉토리를 통해 백엔드에 대한 처리를 수행할 수 있다.

                                                                                                                  +Rain Sugar BangBang Front-End

                                                                                                                  Rain Sugar BangBang

                                                                                                                  nextjs

                                                                                                                  NextJS에서 API 디렉토리가 사용되지 않는 이유

                                                                                                                  2023-04-30

                                                                                                                  NextJS는 pages/api 디렉토리를 통해 백엔드에 대한 처리를 수행할 수 있다.

                                                                                                                  하지만 정적으로 배포된 사이트(ex. 블로그 등)에서는 pages/api 등과 같은 동적 로직은 지원되지 않기 때문에 api 디렉토리를 사용할 수 없다.

                                                                                                                  정적 배포 방법

                                                                                                                  // package.json
                                                                                                                  @@ -25,4 +25,4 @@
                                                                                                                   module.exports = nextConfig

                                                                                                                  참조

                                                                                                                  -

                                                                                                                  https://nextjs.org/docs/advanced-features/static-html-export

                                                                                                                  "nextjs" 카테고리의 다른 글

                                                                                                                    COMMENTS

                                                                                                                    \ No newline at end of file +

                                                                                                                    https://nextjs.org/docs/advanced-features/static-html-export

                                                                                                                    "nextjs" 카테고리의 다른 글

                                                                                                                      COMMENTS

                                                                                                                      \ No newline at end of file diff --git a/nextjs-prop-classname-did-not-match.html b/nextjs-prop-classname-did-not-match.html index 49bf2d85..c25ade4f 100644 --- a/nextjs-prop-classname-did-not-match.html +++ b/nextjs-prop-classname-did-not-match.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                      Rain Sugar BangBang

                                                                                                                      nextjs

                                                                                                                      prop `classname` did not match. with styled-components

                                                                                                                      2023-04-30

                                                                                                                      prop 'classname' did not match. with styled-components 에러 해결 방법

                                                                                                                      +Rain Sugar BangBang Front-End

                                                                                                                      Rain Sugar BangBang

                                                                                                                      nextjs

                                                                                                                      prop `classname` did not match. with styled-components

                                                                                                                      2023-04-30

                                                                                                                      prop 'classname' did not match. with styled-components 에러 해결 방법

                                                                                                                      NextJS의 next.config를 수정해준다.

                                                                                                                      /** @type {import('next').NextConfig} */
                                                                                                                       const nextConfig = {
                                                                                                                      @@ -8,4 +8,4 @@
                                                                                                                         },
                                                                                                                       };
                                                                                                                       
                                                                                                                      -module.exports = nextConfig;

                                                                                                                      "nextjs" 카테고리의 다른 글

                                                                                                                        COMMENTS

                                                                                                                        \ No newline at end of file +module.exports = nextConfig;

                                                                                                                        "nextjs" 카테고리의 다른 글

                                                                                                                          COMMENTS

                                                                                                                          \ No newline at end of file diff --git a/nextjs-react-responsive.html b/nextjs-react-responsive.html index 15c9d91b..def2b648 100644 --- a/nextjs-react-responsive.html +++ b/nextjs-react-responsive.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                          Rain Sugar BangBang

                                                                                                                          nextjs

                                                                                                                          nextjs에 react responsive 적용하기

                                                                                                                          2023-03-04

                                                                                                                          이번에 새로 만들고 있는 토이 프로젝트를 작업하던 중, 모바일 화면일때만 화면을 보여주고 싶었다. 단, 모든 컴포넌트에 미디어쿼리를 통해 작성해줄 수는 없기에 react-responsive를 적용해 화면이 481px을 넘어가면 고정된 페이지를 보여줄 수 있도록 코드를 작성했다.

                                                                                                                          +Rain Sugar BangBang Front-End

                                                                                                                          Rain Sugar BangBang

                                                                                                                          nextjs

                                                                                                                          nextjs에 react responsive 적용하기

                                                                                                                          2023-03-04

                                                                                                                          이번에 새로 만들고 있는 토이 프로젝트를 작업하던 중, 모바일 화면일때만 화면을 보여주고 싶었다. 단, 모든 컴포넌트에 미디어쿼리를 통해 작성해줄 수는 없기에 react-responsive를 적용해 화면이 481px을 넘어가면 고정된 페이지를 보여줄 수 있도록 코드를 작성했다.

                                                                                                                          react-responsive 설치

                                                                                                                          yarn add react-responsive

                                                                                                                          hook 설정

                                                                                                                          @@ -86,4 +86,4 @@

                                                                                                                          코드 수정

                                                                                                                          useMideaQuery가 window 화면을 기준으로 동작하기 때문에,

                                                                                                                          공식문서의 예제와 비슷하게 useState를 통한 boolean 값으로 화면을 노출시켜줬다.

                                                                                                                          위의 방식을 적용하니 hydration 에러 없이 잘 작동하는 모습을 볼 수 있었다.

                                                                                                                          -

                                                                                                                          image

                                                                                                                          "nextjs" 카테고리의 다른 글

                                                                                                                            COMMENTS

                                                                                                                            \ No newline at end of file +

                                                                                                                            image

                                                                                                                            "nextjs" 카테고리의 다른 글

                                                                                                                              COMMENTS

                                                                                                                              \ No newline at end of file diff --git a/nextjs.html b/nextjs.html index 84dc0b34..9aabc583 100644 --- a/nextjs.html +++ b/nextjs.html @@ -1 +1 @@ -Rain Sugar BangBang Front-End \ No newline at end of file +Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/profile.html b/profile.html index 4df69b60..e3fab61c 100644 --- a/profile.html +++ b/profile.html @@ -1 +1 @@ -
                                                                                                                              Back to Blog
                                                                                                                              Welcome To My Profile
                                                                                                                              profileYo Han (26)

                                                                                                                              Introduction

                                                                                                                              • 안녕하세요! 프론트엔드 개발자 Yohan입니다.
                                                                                                                              • 실질적인 문제 해결에 기여한다는 매력에 이끌려 개발자의 길을 걷고 있습니다.
                                                                                                                              • 작은 것이라도 새롭게 배운 것은 Github나 블로그를 통해 꾸준히 기록합니다.
                                                                                                                              • 좋은 코드에 대해 끊임없이 고민하고 있습니다.
                                                                                                                              • 개인의 성장에 많은 관심이 있습니다.

                                                                                                                              Contact & Channel

                                                                                                                              Connect : yhl0078@gmail.com

                                                                                                                              Skills

                                                                                                                              Languages

                                                                                                                              stackCardstackCardstackCardstackCard

                                                                                                                              FrameWorks

                                                                                                                              stackCardstackCard

                                                                                                                              Librarys

                                                                                                                              stackCardstackCardstackCardstackCard

                                                                                                                              DevOps

                                                                                                                              stackCardstackCardstackCardstackCard

                                                                                                                              Now Learning...

                                                                                                                              stackCardstackCard

                                                                                                                              Projects

                                                                                                                              EVER-RENT

                                                                                                                              개인 간 중고 물품 렌탈 거래 플랫폼 개발

                                                                                                                              팀원 (React: 3명 / Spring 2명)

                                                                                                                                핵심경험

                                                                                                                              • Kakao map API를 이용한 위치 정보 조회
                                                                                                                              • Redux/toolkit을 이용한 글 목록 구현 (무한 스크롤 / 페이지네이션)
                                                                                                                              • 카테고리, 지역별 제품 필터링, 검색 기능 연동
                                                                                                                              • LoadingSpinner, SkeletonUI 제작
                                                                                                                              • React-datepicker를 이용한 custom calendar 제작
                                                                                                                              • React-responsive(mediaQuery)를 이용한 반응형 웹 적용
                                                                                                                              • Github Link

                                                                                                                                핵심경험

                                                                                                                              • 기능 구현 코드 복습과 더 나은 코드에 대한 고민
                                                                                                                              • 부드러운 다크모드 전환 구현
                                                                                                                              • 기능별 컴포넌트의 분리를 통해 라이브러리와 유사한 틀 구성
                                                                                                                              • Github Link

                                                                                                                              ToyCode

                                                                                                                              라이브러리를 사용하지 않은 간단한 기능 구현

                                                                                                                              개인 프로젝트

                                                                                                                              Personal Experience

                                                                                                                              Now Doing

                                                                                                                              개인 프로젝트

                                                                                                                            • 나혼밥 레시피 - 혼자 사는 사람들의 레시피 공유 사이트- github Link
                                                                                                                            • GITHUB Blog - 개인 블로그 개발- github Link
                                                                                                                            • ToyCode - 간단한 기능 복습- github Link
                                                                                                                            • 코드스니펫 - 반복되는 코드를 최소화하기 위한 간단한 스니펫 제작- github Link
                                                                                                                            • eggseggseggseggseggs
                                                                                                                              Click Me!

                                                                                                                              또 와주셨네요!

                                                                                                                              블로그를 먼저 보시려면 좌측 상단의 버튼을 눌러주세요!

                                                                                                                              아직 많이 부족하지만 점점 채우고 있습니다!

                                                                                                                            • 자유롭게 의견을 남겨주신다면 미리 감사드립니다!!
                                                                                                                            • \ No newline at end of file +
                                                                                                                              Back to Blog
                                                                                                                              Welcome To My Profile
                                                                                                                              profileYo Han (26)

                                                                                                                              Introduction

                                                                                                                              • 안녕하세요! 프론트엔드 개발자 Yohan입니다.
                                                                                                                              • 실질적인 문제 해결에 기여한다는 매력에 이끌려 개발자의 길을 걷고 있습니다.
                                                                                                                              • 작은 것이라도 새롭게 배운 것은 Github나 블로그를 통해 꾸준히 기록합니다.
                                                                                                                              • 좋은 코드에 대해 끊임없이 고민하고 있습니다.
                                                                                                                              • 개인의 성장에 많은 관심이 있습니다.

                                                                                                                              Contact & Channel

                                                                                                                              Connect : yhl0078@gmail.com

                                                                                                                              Skills

                                                                                                                              Languages

                                                                                                                              stackCardstackCardstackCardstackCard

                                                                                                                              FrameWorks

                                                                                                                              stackCardstackCard

                                                                                                                              Librarys

                                                                                                                              stackCardstackCardstackCardstackCard

                                                                                                                              DevOps

                                                                                                                              stackCardstackCardstackCardstackCard

                                                                                                                              Now Learning...

                                                                                                                              stackCardstackCard

                                                                                                                              Projects

                                                                                                                              EVER-RENT

                                                                                                                              개인 간 중고 물품 렌탈 거래 플랫폼 개발

                                                                                                                              팀원 (React: 3명 / Spring 2명)

                                                                                                                                핵심경험

                                                                                                                              • Kakao map API를 이용한 위치 정보 조회
                                                                                                                              • Redux/toolkit을 이용한 글 목록 구현 (무한 스크롤 / 페이지네이션)
                                                                                                                              • 카테고리, 지역별 제품 필터링, 검색 기능 연동
                                                                                                                              • LoadingSpinner, SkeletonUI 제작
                                                                                                                              • React-datepicker를 이용한 custom calendar 제작
                                                                                                                              • React-responsive(mediaQuery)를 이용한 반응형 웹 적용
                                                                                                                              • Github Link

                                                                                                                                핵심경험

                                                                                                                              • 기능 구현 코드 복습과 더 나은 코드에 대한 고민
                                                                                                                              • 부드러운 다크모드 전환 구현
                                                                                                                              • 기능별 컴포넌트의 분리를 통해 라이브러리와 유사한 틀 구성
                                                                                                                              • Github Link

                                                                                                                              ToyCode

                                                                                                                              라이브러리를 사용하지 않은 간단한 기능 구현

                                                                                                                              개인 프로젝트

                                                                                                                              Personal Experience

                                                                                                                              Now Doing

                                                                                                                              개인 프로젝트

                                                                                                                            • 나혼밥 레시피 - 혼자 사는 사람들의 레시피 공유 사이트- github Link
                                                                                                                            • GITHUB Blog - 개인 블로그 개발- github Link
                                                                                                                            • ToyCode - 간단한 기능 복습- github Link
                                                                                                                            • 코드스니펫 - 반복되는 코드를 최소화하기 위한 간단한 스니펫 제작- github Link
                                                                                                                            • eggseggseggseggseggs
                                                                                                                              Click Me!

                                                                                                                              또 와주셨네요!

                                                                                                                              블로그를 먼저 보시려면 좌측 상단의 버튼을 눌러주세요!

                                                                                                                              아직 많이 부족하지만 점점 채우고 있습니다!

                                                                                                                            • 자유롭게 의견을 남겨주신다면 미리 감사드립니다!!
                                                                                                                            • \ No newline at end of file diff --git a/react-common-components.html b/react-common-components.html index 7adf4a9a..60578e18 100644 --- a/react-common-components.html +++ b/react-common-components.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                              Rain Sugar BangBang

                                                                                                                              react

                                                                                                                              React 공통 컴포넌트 제작 (input, button)

                                                                                                                              2023-04-13

                                                                                                                              공통 컴포넌트 제작의 필요성

                                                                                                                              +Rain Sugar BangBang Front-End

                                                                                                                              Rain Sugar BangBang

                                                                                                                              react

                                                                                                                              React 공통 컴포넌트 제작 (input, button)

                                                                                                                              2023-04-13

                                                                                                                              공통 컴포넌트 제작의 필요성

                                                                                                                              하나의 사이트를 제작할때 비슷한 input이나 button을 사용해야 하는 상황이 자주 있는데, 공통된 컴포넌트를 만들어 놓으면 코드 중복을 줄이면서 상황에 맞춰 적절하게 사용할 수 있게 된다.

                                                                                                                              @@ -149,4 +149,4 @@

                                                                                                                              Button

                                                                                                                              &:hover { background-color: ${props => props.theme.mainButtonHover}; } -`;

                                                                                                                              "react" 카테고리의 다른 글

                                                                                                                                COMMENTS

                                                                                                                                \ No newline at end of file +`;

                                                                                                                                "react" 카테고리의 다른 글

                                                                                                                                  COMMENTS

                                                                                                                                  \ No newline at end of file diff --git a/react-component-lifecycle.html b/react-component-lifecycle.html index ccdf9c07..1647e91b 100644 --- a/react-component-lifecycle.html +++ b/react-component-lifecycle.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                  Rain Sugar BangBang

                                                                                                                                  react

                                                                                                                                  React 함수형 컴포넌트와 Class형 컴포넌트 생명주기

                                                                                                                                  2023-03-23

                                                                                                                                  React state와 LifeCycle

                                                                                                                                  +Rain Sugar BangBang Front-End

                                                                                                                                  Rain Sugar BangBang

                                                                                                                                  \ No newline at end of file +

                                                                                                                                  https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html

                                                                                                                                  "react" 카테고리의 다른 글

                                                                                                                                    COMMENTS

                                                                                                                                    \ No newline at end of file diff --git a/react-context-api.html b/react-context-api.html index 92f6fae8..94271080 100644 --- a/react-context-api.html +++ b/react-context-api.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                    Rain Sugar BangBang

                                                                                                                                    react

                                                                                                                                    ContextAPI 사용하기

                                                                                                                                    2023-03-15

                                                                                                                                    ContextAPI

                                                                                                                                    +Rain Sugar BangBang Front-End

                                                                                                                                    Rain Sugar BangBang

                                                                                                                                    react

                                                                                                                                    ContextAPI 사용하기

                                                                                                                                    2023-03-15

                                                                                                                                    ContextAPI

                                                                                                                                    간단한 데이터의 변화를 props drilling 없이 전역적으로 관리해주기 위해 ContextAPI를 사용해볼 수 있다.

                                                                                                                                    @@ -126,4 +126,4 @@

                                                                                                                                    TypeScript에서는 context 초기값 등의 설정이 필요하다.

                                                                                                                                    if (posts) { update(posts); } - }, [posts, update]);

                                                                                                                                    "react" 카테고리의 다른 글

                                                                                                                                      COMMENTS

                                                                                                                                      \ No newline at end of file + }, [posts, update]);

                                                                                                                                      "react" 카테고리의 다른 글

                                                                                                                                        COMMENTS

                                                                                                                                        \ No newline at end of file diff --git a/react-datepicker.html b/react-datepicker.html index 95600951..d02fb624 100644 --- a/react-datepicker.html +++ b/react-datepicker.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                        Rain Sugar BangBang

                                                                                                                                        react

                                                                                                                                        React datepicker 사용하기

                                                                                                                                        2023-03-03

                                                                                                                                        React Datepicker 사용하기

                                                                                                                                        +Rain Sugar BangBang Front-End

                                                                                                                                        Rain Sugar BangBang

                                                                                                                                        react

                                                                                                                                        React datepicker 사용하기

                                                                                                                                        2023-03-03

                                                                                                                                        React Datepicker 사용하기

                                                                                                                                        공식 문서

                                                                                                                                        https://reactdatepicker.com/

                                                                                                                                        package 설치

                                                                                                                                        @@ -67,4 +67,4 @@

                                                                                                                                        스타일 적용

                                                                                                                                        /* ... */ -

                                                                                                                                        코드가 길어지더라도 jsx와 철저하게 분리하고 싶다면 이런 방법도 나쁘지 않은 것 같다.

                                                                                                                                        "react" 카테고리의 다른 글

                                                                                                                                          COMMENTS

                                                                                                                                          \ No newline at end of file +

                                                                                                                                          코드가 길어지더라도 jsx와 철저하게 분리하고 싶다면 이런 방법도 나쁘지 않은 것 같다.

                                                                                                                                          "react" 카테고리의 다른 글

                                                                                                                                            COMMENTS

                                                                                                                                            \ No newline at end of file diff --git a/react-media-query.html b/react-media-query.html index 347894fb..1d00ec8f 100644 --- a/react-media-query.html +++ b/react-media-query.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                            Rain Sugar BangBang

                                                                                                                                            react

                                                                                                                                            React mediaQuery 사용법

                                                                                                                                            2023-03-03

                                                                                                                                            react-responsive 사용해보기

                                                                                                                                            +Rain Sugar BangBang Front-End

                                                                                                                                            Rain Sugar BangBang

                                                                                                                                            react

                                                                                                                                            React mediaQuery 사용법

                                                                                                                                            2023-03-03

                                                                                                                                            react-responsive 사용해보기

                                                                                                                                            공식 사이트 : https://yarnpkg.com/package/react-responsive

                                                                                                                                            CSS의 mediaQuery를 사용해볼 수도 있지만, 간편하게 적용할 수 있는 패키지가 있어서 사용해봤다.

                                                                                                                                            패키지 설치

                                                                                                                                            @@ -43,4 +43,4 @@

                                                                                                                                            useMediaQuery 사용

                                                                                                                                            </> ); }; -

                                                                                                                                            편의상 한 컴포넌트에 다 집어넣을 수도 있지만, 모바일과 데스크탑 환경의 view는 아예 다른 경우가 많기 때문에, 코드 관리를 편하게 하기 위해서는 모바일 컴포넌트를 따로 만드는게 더 나을 것 같다는 생각이 든다.

                                                                                                                                            "react" 카테고리의 다른 글

                                                                                                                                              COMMENTS

                                                                                                                                              \ No newline at end of file +

                                                                                                                                              편의상 한 컴포넌트에 다 집어넣을 수도 있지만, 모바일과 데스크탑 환경의 view는 아예 다른 경우가 많기 때문에, 코드 관리를 편하게 하기 위해서는 모바일 컴포넌트를 따로 만드는게 더 나을 것 같다는 생각이 든다.

                                                                                                                                              "react" 카테고리의 다른 글

                                                                                                                                                COMMENTS

                                                                                                                                                \ No newline at end of file diff --git a/react-memo.html b/react-memo.html index 5c09657c..39696864 100644 --- a/react-memo.html +++ b/react-memo.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                Rain Sugar BangBang

                                                                                                                                                react

                                                                                                                                                React.memo() 를 통한 컴포넌트 최적화

                                                                                                                                                2023-03-16
                                                                                                                                                +Rain Sugar BangBang Front-End

                                                                                                                                                Rain Sugar BangBang

                                                                                                                                                \ No newline at end of file +

                                                                                                                                                https://ui.toast.com/weekly-pick/ko_20190731

                                                                                                                                                "react" 카테고리의 다른 글

                                                                                                                                                  COMMENTS

                                                                                                                                                  \ No newline at end of file diff --git a/react-modal-non-library.html b/react-modal-non-library.html index b4078f65..291d1779 100644 --- a/react-modal-non-library.html +++ b/react-modal-non-library.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                  Rain Sugar BangBang

                                                                                                                                                  react

                                                                                                                                                  라이브러리 없이 React Modal 만들기

                                                                                                                                                  2023-03-03

                                                                                                                                                  라이브러리 없이 모달창 만들어보기

                                                                                                                                                  +Rain Sugar BangBang Front-End

                                                                                                                                                  Rain Sugar BangBang

                                                                                                                                                  react

                                                                                                                                                  라이브러리 없이 React Modal 만들기

                                                                                                                                                  2023-03-03

                                                                                                                                                  라이브러리 없이 모달창 만들어보기

                                                                                                                                                  모달 버튼이 있는 페이지

                                                                                                                                                  // ModalTest.jsx
                                                                                                                                                   
                                                                                                                                                  @@ -73,4 +73,4 @@
                                                                                                                                                   `;

                                                                                                                                                  브라우저 화면

                                                                                                                                                  image

                                                                                                                                                  -

                                                                                                                                                  스타일은 용도에 맞게 다양하게 변경시킬 수 있다.

                                                                                                                                                  "react" 카테고리의 다른 글

                                                                                                                                                    COMMENTS

                                                                                                                                                    \ No newline at end of file +

                                                                                                                                                    스타일은 용도에 맞게 다양하게 변경시킬 수 있다.

                                                                                                                                                    "react" 카테고리의 다른 글

                                                                                                                                                      COMMENTS

                                                                                                                                                      \ No newline at end of file diff --git a/react-navigate-props.html b/react-navigate-props.html index 21955b4c..929ecc97 100644 --- a/react-navigate-props.html +++ b/react-navigate-props.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                      Rain Sugar BangBang

                                                                                                                                                      react

                                                                                                                                                      React navigate로 props 넘기기

                                                                                                                                                      2023-03-03

                                                                                                                                                      useNavigate를 이용한 props 전달

                                                                                                                                                      +Rain Sugar BangBang Front-End

                                                                                                                                                      Rain Sugar BangBang

                                                                                                                                                      react

                                                                                                                                                      React navigate로 props 넘기기

                                                                                                                                                      2023-03-03

                                                                                                                                                      useNavigate를 이용한 props 전달

                                                                                                                                                      useNavigate로 다른 페이지의 화면으로 이동할 때, props처럼 값을 넘기는 방법을 사용해볼 수 있다.

                                                                                                                                                      
                                                                                                                                                       // 보내는컴포넌트.jsx
                                                                                                                                                      @@ -32,4 +32,4 @@ 

                                                                                                                                                      useNavigate로 다른 페이지의 화면으로 이동할 때, props처럼

                                                                                                                                                      위와 같이 작성은 아래와 같이 데이터를 받아오는 것을 보여준다.

                                                                                                                                                      image

                                                                                                                                                      -

                                                                                                                                                      하지만 데이터를 받는 페이지가 직접적으로 접속할 수 있는 페이지라면 그렇게 접속한 페이지는 아무 데이터가 없는 화면으로 노출될 수 있기 때문에, 전역 상태를 관리해줄지, 데이터를 넘겨줄지 상황에 알맞는 방식을 적용하는 것이 중요하다.

                                                                                                                                                      "react" 카테고리의 다른 글

                                                                                                                                                        COMMENTS

                                                                                                                                                        \ No newline at end of file +

                                                                                                                                                        하지만 데이터를 받는 페이지가 직접적으로 접속할 수 있는 페이지라면 그렇게 접속한 페이지는 아무 데이터가 없는 화면으로 노출될 수 있기 때문에, 전역 상태를 관리해줄지, 데이터를 넘겨줄지 상황에 알맞는 방식을 적용하는 것이 중요하다.

                                                                                                                                                        "react" 카테고리의 다른 글

                                                                                                                                                          COMMENTS

                                                                                                                                                          \ No newline at end of file diff --git a/react-simple-formdata-code.html b/react-simple-formdata-code.html index a9da48ee..fc4bed82 100644 --- a/react-simple-formdata-code.html +++ b/react-simple-formdata-code.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                          Rain Sugar BangBang

                                                                                                                                                          react

                                                                                                                                                          react formdata 코드간소화

                                                                                                                                                          2023-03-03

                                                                                                                                                          FormData 저장 코드 간소화하기

                                                                                                                                                          +Rain Sugar BangBang Front-End

                                                                                                                                                          Rain Sugar BangBang

                                                                                                                                                          react

                                                                                                                                                          react formdata 코드간소화

                                                                                                                                                          2023-03-03

                                                                                                                                                          FormData 저장 코드 간소화하기

                                                                                                                                                          하나의 폼에서 여러 개의 input 값을 변경하기 위해 함수를 만들어 간단하게 코드를 간소화시킬 수 있다.

                                                                                                                                                          // form.tsx
                                                                                                                                                           <Container onSubmit={e => onSubmitHandler(e, dispatch)}>
                                                                                                                                                          @@ -69,4 +69,4 @@
                                                                                                                                                             const { name, value } = e.target.name;
                                                                                                                                                             setFormData(prev => ({ ...prev, [name]: value }));
                                                                                                                                                           };
                                                                                                                                                          -

                                                                                                                                                          html input의 name 속성을 이용해 setState 코드를 간소화시킬 수 있는 방법이다.

                                                                                                                                                          "react" 카테고리의 다른 글

                                                                                                                                                            COMMENTS

                                                                                                                                                            \ No newline at end of file +

                                                                                                                                                            html input의 name 속성을 이용해 setState 코드를 간소화시킬 수 있는 방법이다.

                                                                                                                                                            "react" 카테고리의 다른 글

                                                                                                                                                              COMMENTS

                                                                                                                                                              \ No newline at end of file diff --git a/react-smooth-scroll.html b/react-smooth-scroll.html index 1d227143..2d09ab3a 100644 --- a/react-smooth-scroll.html +++ b/react-smooth-scroll.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                              Rain Sugar BangBang

                                                                                                                                                              react

                                                                                                                                                              React/Javascript 부드러운 스크롤 이동 적용

                                                                                                                                                              2023-03-25

                                                                                                                                                              부드러운 스크롤 이동 적용 방법

                                                                                                                                                              +Rain Sugar BangBang Front-End

                                                                                                                                                              Rain Sugar BangBang

                                                                                                                                                              react

                                                                                                                                                              React/Javascript 부드러운 스크롤 이동 적용

                                                                                                                                                              2023-03-25

                                                                                                                                                              부드러운 스크롤 이동 적용 방법

                                                                                                                                                              behavior:"smooth" 속성을 scroll 이벤트와 scroll 메서드와 같이 사용하며 부드러운 스크롤 이동을 적용해볼 수 있다.

                                                                                                                                                              @@ -208,4 +208,4 @@

                                                                                                                                                              a 태그를 통한 스크롤 이동

                                                                                                                                                              https://developer.mozilla.org/en-US/docs/Web/API/Window/scroll

                                                                                                                                                              requestAnimationFrame

                                                                                                                                                              https://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame

                                                                                                                                                              -

                                                                                                                                                              https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp

                                                                                                                                                              "react" 카테고리의 다른 글

                                                                                                                                                                COMMENTS

                                                                                                                                                                \ No newline at end of file +

                                                                                                                                                                https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp

                                                                                                                                                                "react" 카테고리의 다른 글

                                                                                                                                                                  COMMENTS

                                                                                                                                                                  \ No newline at end of file diff --git a/react-styled-components-hover.html b/react-styled-components-hover.html index a9c9a3d2..eef859c9 100644 --- a/react-styled-components-hover.html +++ b/react-styled-components-hover.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                  Rain Sugar BangBang

                                                                                                                                                                  react

                                                                                                                                                                  react에 styled-components를 이용해 hover 적용하기

                                                                                                                                                                  2023-03-03

                                                                                                                                                                  리액트 스타일 컴포넌트에 hover 적용하기

                                                                                                                                                                  +Rain Sugar BangBang Front-End

                                                                                                                                                                  Rain Sugar BangBang

                                                                                                                                                                  react

                                                                                                                                                                  react에 styled-components를 이용해 hover 적용하기

                                                                                                                                                                  2023-03-03

                                                                                                                                                                  리액트 스타일 컴포넌트에 hover 적용하기

                                                                                                                                                                  // &:hover를 넣는다.
                                                                                                                                                                   
                                                                                                                                                                   const ButtonStyle = styled.button`
                                                                                                                                                                  @@ -24,4 +24,4 @@
                                                                                                                                                                     /* 마우스로 링크를 클릭하고 뗄 때까지의 스타일 */
                                                                                                                                                                     &:active {
                                                                                                                                                                     }
                                                                                                                                                                  -`;

                                                                                                                                                                  "react" 카테고리의 다른 글

                                                                                                                                                                    COMMENTS

                                                                                                                                                                    \ No newline at end of file +`;

                                                                                                                                                                    "react" 카테고리의 다른 글

                                                                                                                                                                      COMMENTS

                                                                                                                                                                      \ No newline at end of file diff --git a/react-submit-prevent-default.html b/react-submit-prevent-default.html index ce55030f..d1701f32 100644 --- a/react-submit-prevent-default.html +++ b/react-submit-prevent-default.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                      Rain Sugar BangBang

                                                                                                                                                                      react

                                                                                                                                                                      form 태그에서 submit 이벤트 방지하기

                                                                                                                                                                      2023-03-03

                                                                                                                                                                      react form에서 submit 이벤트 방지하기

                                                                                                                                                                      +Rain Sugar BangBang Front-End

                                                                                                                                                                      Rain Sugar BangBang

                                                                                                                                                                      react

                                                                                                                                                                      form 태그에서 submit 이벤트 방지하기

                                                                                                                                                                      2023-03-03

                                                                                                                                                                      react form에서 submit 이벤트 방지하기

                                                                                                                                                                      submit 이벤트가 발생하면 페이지가 새로고침이 된다.

                                                                                                                                                                      단순히 페이지가 리프레시 되는 것도 좋은 사용자 경험이 아닌데, 임시로 저장돼있던 데이터(회원가입 정보 등)가 날아가면 React의 작동 방식을 거스를 뿐 아니라 사용자 경험에도 치명적이다.

                                                                                                                                                                      떄문에 우리는 sumbit 이벤트를 멈춰줄 필요가 있다.

                                                                                                                                                                      @@ -17,4 +17,4 @@

                                                                                                                                                                      submit 이벤트가 발생하면 페이지가 새로고침이 된다.

                                                                                                                                                                      </form> ); }; -

                                                                                                                                                                      위와 같이 작성하면 같은 폼 안에 있는 경우에 대해 submit 방지가 잘 되는 것을 확인할 수 있다.

                                                                                                                                                                      "react" 카테고리의 다른 글

                                                                                                                                                                        COMMENTS

                                                                                                                                                                        \ No newline at end of file +

                                                                                                                                                                        위와 같이 작성하면 같은 폼 안에 있는 경우에 대해 submit 방지가 잘 되는 것을 확인할 수 있다.

                                                                                                                                                                        "react" 카테고리의 다른 글

                                                                                                                                                                          COMMENTS

                                                                                                                                                                          \ No newline at end of file diff --git a/react-type-assertion.html b/react-type-assertion.html index 190baecc..753a36b9 100644 --- a/react-type-assertion.html +++ b/react-type-assertion.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                          Rain Sugar BangBang

                                                                                                                                                                          react

                                                                                                                                                                          React Form event type (feat. 타입 단언 as)

                                                                                                                                                                          2023-04-13

                                                                                                                                                                          Form Event Type

                                                                                                                                                                          +Rain Sugar BangBang Front-End

                                                                                                                                                                          Rain Sugar BangBang

                                                                                                                                                                          react

                                                                                                                                                                          React Form event type (feat. 타입 단언 as)

                                                                                                                                                                          2023-04-13

                                                                                                                                                                          Form Event Type

                                                                                                                                                                          우리는 로그인이나 포스팅 등의 화면을 만들어줄 때 클라이언트가 입력하는 데이터를 입력받는 방법 중 하나로 onChange 등의 FormEvent를 이용해 원하는 값을 추출할 수 있다. 이때 이벤트 타입은 아래와 같이 사용될 수 있다.

                                                                                                                                                                          import { FormEvent } from "react";
                                                                                                                                                                           
                                                                                                                                                                          @@ -35,4 +35,4 @@ 

                                                                                                                                                                          주의사항

                                                                                                                                                                          if(someMutation){ // ... nest logic } -}

                                                                                                                                                                          "react" 카테고리의 다른 글

                                                                                                                                                                            COMMENTS

                                                                                                                                                                            \ No newline at end of file +}

                                                                                                                                                                            "react" 카테고리의 다른 글

                                                                                                                                                                              COMMENTS

                                                                                                                                                                              \ No newline at end of file diff --git a/react-usecallback.html b/react-usecallback.html index 8fa30e89..3bc68b0b 100644 --- a/react-usecallback.html +++ b/react-usecallback.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                              Rain Sugar BangBang

                                                                                                                                                                              react

                                                                                                                                                                              useCallback을 이용한 컴포넌트 최적화하기

                                                                                                                                                                              2023-03-16

                                                                                                                                                                              useCallback이란?

                                                                                                                                                                              +Rain Sugar BangBang Front-End

                                                                                                                                                                              Rain Sugar BangBang

                                                                                                                                                                              react

                                                                                                                                                                              useCallback을 이용한 컴포넌트 최적화하기

                                                                                                                                                                              2023-03-16

                                                                                                                                                                              useCallback이란?

                                                                                                                                                                              특정 함수를 새로 만들지 않고 재사용하고 싶을때 사용하는 함수 메모이제이션용 React hook이다.

                                                                                                                                                                              @@ -156,4 +156,4 @@

                                                                                                                                                                              useCallback 남용 시 문제점

                                                                                                                                                                              memo, useMemo, useCallback 등 어떤 Memoization이나 부적절하게 사용할 경우 오히려 성능이 떨어질 수 있기 때문에 상황에 맞게 적절하게 사용하는 것이 좋은 것 같다.


                                                                                                                                                                              -

                                                                                                                                                                              참조: https://beta.reactjs.org/reference/react/useCallback

                                                                                                                                                                              "react" 카테고리의 다른 글

                                                                                                                                                                                COMMENTS

                                                                                                                                                                                \ No newline at end of file +

                                                                                                                                                                                참조: https://beta.reactjs.org/reference/react/useCallback

                                                                                                                                                                                "react" 카테고리의 다른 글

                                                                                                                                                                                  COMMENTS

                                                                                                                                                                                  \ No newline at end of file diff --git a/react-usememo.html b/react-usememo.html index c2759799..2a3ae204 100644 --- a/react-usememo.html +++ b/react-usememo.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                                  Rain Sugar BangBang

                                                                                                                                                                                  react

                                                                                                                                                                                  useMemo를 이용한 컴포넌트 최적화하기

                                                                                                                                                                                  2023-03-16

                                                                                                                                                                                  useMemo란?

                                                                                                                                                                                  +Rain Sugar BangBang Front-End

                                                                                                                                                                                  Rain Sugar BangBang

                                                                                                                                                                                  react

                                                                                                                                                                                  useMemo를 이용한 컴포넌트 최적화하기

                                                                                                                                                                                  2023-03-16

                                                                                                                                                                                  useMemo란?

                                                                                                                                                                                  useMemo는 React.memo(), useCallback과 같이 컴포넌트를 최적화하기 위한 훅 중 하나이다.

                                                                                                                                                                                  @@ -124,4 +124,4 @@

                                                                                                                                                                                  연산 비용이 비싸다는 기준은?

                                                                                                                                                                                  실제로 React의 성능 문제 대부분은 Effect에서 발생하는 업데이트 체인으로 인해 발생한다고 한다.


                                                                                                                                                                                  -

                                                                                                                                                                                  참조 : https://beta.reactjs.org/reference/react/useMemo

                                                                                                                                                                                  "react" 카테고리의 다른 글

                                                                                                                                                                                    COMMENTS

                                                                                                                                                                                    \ No newline at end of file +

                                                                                                                                                                                    참조 : https://beta.reactjs.org/reference/react/useMemo

                                                                                                                                                                                    "react" 카테고리의 다른 글

                                                                                                                                                                                      COMMENTS

                                                                                                                                                                                      \ No newline at end of file diff --git a/react-z-index-error.html b/react-z-index-error.html index 0331980a..d80923cc 100644 --- a/react-z-index-error.html +++ b/react-z-index-error.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                                      Rain Sugar BangBang

                                                                                                                                                                                      react

                                                                                                                                                                                      z-index가 올바르게 적용되지 않을때 적용해볼 수 있는 방법

                                                                                                                                                                                      2023-03-03

                                                                                                                                                                                      z-index 미적용시 해결방법

                                                                                                                                                                                      +Rain Sugar BangBang Front-End

                                                                                                                                                                                      Rain Sugar BangBang

                                                                                                                                                                                      react

                                                                                                                                                                                      z-index가 올바르게 적용되지 않을때 적용해볼 수 있는 방법

                                                                                                                                                                                      2023-03-03

                                                                                                                                                                                      z-index 미적용시 해결방법

                                                                                                                                                                                      image

                                                                                                                                                                                      위와 같이 z-index를 999로 적용해도 원하는대로 작동하지 않는 경우를 볼 수 있다.

                                                                                                                                                                                      MDN 공식 문서 : https://developer.mozilla.org/ko/docs/Web/CSS/z-index

                                                                                                                                                                                      @@ -17,4 +17,4 @@

                                                                                                                                                                                      z-index가 제대로 작동하지 않을때 참고할만한 사항

                                                                                                                                                                                    • opacity나 transform과 같은 css 속성이 설정돼있는지 확인한다. 해당 css요소가 설정돼있다면 1번의 기준에 맞춰 코드를 다시 작성해준다.
                                                                                                                                                                                    • 이정도면 버그를 수정하는데 충분하지만 더 싶도 깊은 내용은 아래를 참고해보면 좋을 것 같다.

                                                                                                                                                                                      -

                                                                                                                                                                                      참조 : https://coder-coder.com/z-index-isnt-working/

                                                                                                                                                                                      "react" 카테고리의 다른 글

                                                                                                                                                                                        COMMENTS

                                                                                                                                                                                        \ No newline at end of file +

                                                                                                                                                                                        참조 : https://coder-coder.com/z-index-isnt-working/

                                                                                                                                                                                        "react" 카테고리의 다른 글

                                                                                                                                                                                          COMMENTS

                                                                                                                                                                                          \ No newline at end of file diff --git a/react.html b/react.html index 2843d38a..8c022ff1 100644 --- a/react.html +++ b/react.html @@ -1 +1 @@ -Rain Sugar BangBang Front-End \ No newline at end of file +Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/redux-mock-server.html b/redux-mock-server.html index e2a823fa..89984da2 100644 --- a/redux-mock-server.html +++ b/redux-mock-server.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                                          Rain Sugar BangBang

                                                                                                                                                                                          redux

                                                                                                                                                                                          postman을 이용해 redux Mock server(json-server) 사용해보기

                                                                                                                                                                                          2023-03-04

                                                                                                                                                                                          React를 사용하면서 서버와 연결 전, Post Man을 통해 임시로 서버를 연결해 React와 소통하는 데이터와 view가 어떻게 처리되는지 확인하고 싶을때 사용해볼 수 있다.

                                                                                                                                                                                          +Rain Sugar BangBang Front-End

                                                                                                                                                                                          Rain Sugar BangBang

                                                                                                                                                                                          redux

                                                                                                                                                                                          postman을 이용해 redux Mock server(json-server) 사용해보기

                                                                                                                                                                                          2023-03-04

                                                                                                                                                                                          React를 사용하면서 서버와 연결 전, Post Man을 통해 임시로 서버를 연결해 React와 소통하는 데이터와 view가 어떻게 처리되는지 확인하고 싶을때 사용해볼 수 있다.

                                                                                                                                                                                          postman 링크 : https://www.postman.com/

                                                                                                                                                                                          json-server 설치

                                                                                                                                                                                          yarn add json-server
                                                                                                                                                                                          @@ -71,4 +71,4 @@

                                                                                                                                                                                          http://localhost:3001/comments
                                                                                                                                                                                        • DELETE : http://localhost:3001/comments/1 - id가 1인 comments 삭제하기
                                                                                                                                                                                        • 위와 같은 방식으로 기본적인 동작을 사용해볼 수 있고, header 값이나 body 값 설정 등 다양한 작업을 진행해볼 수 있다.

                                                                                                                                                                                          -

                                                                                                                                                                                          Redux를 사용할때, 해당 데이터들을 추출하여 전역 상태로 관리해보는 등의 작업을 실행할 수 있다.

                                                                                                                                                                                          "redux" 카테고리의 다른 글

                                                                                                                                                                                            COMMENTS

                                                                                                                                                                                            \ No newline at end of file +

                                                                                                                                                                                            Redux를 사용할때, 해당 데이터들을 추출하여 전역 상태로 관리해보는 등의 작업을 실행할 수 있다.

                                                                                                                                                                                            "redux" 카테고리의 다른 글

                                                                                                                                                                                              COMMENTS

                                                                                                                                                                                              \ No newline at end of file diff --git a/redux-toolkit-async-thunk.html b/redux-toolkit-async-thunk.html index a7a0f8b2..6190d0b8 100644 --- a/redux-toolkit-async-thunk.html +++ b/redux-toolkit-async-thunk.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                                              Rain Sugar BangBang

                                                                                                                                                                                              redux

                                                                                                                                                                                              React reduxToolkit과 미들웨어 AsyncThunk 사용해보기

                                                                                                                                                                                              2023-03-04

                                                                                                                                                                                              ReduxToolkit

                                                                                                                                                                                              +Rain Sugar BangBang Front-End

                                                                                                                                                                                              Rain Sugar BangBang

                                                                                                                                                                                              redux

                                                                                                                                                                                              React reduxToolkit과 미들웨어 AsyncThunk 사용해보기

                                                                                                                                                                                              2023-03-04

                                                                                                                                                                                              ReduxToolkit

                                                                                                                                                                                              리덕스 툴킷은 일반적인 리덕스보다 store 설정을 용이하게 해주고, 추가적인 패키지 설치가 필요하지 않게 설계되었으며, 보일러플레이트(여러 군데에서 반복되는 코드)를 최소화하기 위해 만들어졌다.

                                                                                                                                                                                              더 사용하기 쉽다는 의미이다. 이번 글에서는 미들웨어인 AsyncThunk와 함께 사용하는 방법을 확인할 수 있다.

                                                                                                                                                                                              1. 설치

                                                                                                                                                                                              @@ -459,4 +459,4 @@

                                                                                                                                                                                              전체 예제코드

                                                                                                                                                                                              }, }); -export default postsSlice.reducer;

                                                                                                                                                                                              "redux" 카테고리의 다른 글

                                                                                                                                                                                                COMMENTS

                                                                                                                                                                                                \ No newline at end of file +export default postsSlice.reducer;

                                                                                                                                                                                                "redux" 카테고리의 다른 글

                                                                                                                                                                                                  COMMENTS

                                                                                                                                                                                                  \ No newline at end of file diff --git a/redux-typescript-react-reduxtoolkit.html b/redux-typescript-react-reduxtoolkit.html index c595f962..75731300 100644 --- a/redux-typescript-react-reduxtoolkit.html +++ b/redux-typescript-react-reduxtoolkit.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                                                  Rain Sugar BangBang

                                                                                                                                                                                                  redux

                                                                                                                                                                                                  Typescript react에서 ReduxToolkit 사용해보기

                                                                                                                                                                                                  2023-03-04

                                                                                                                                                                                                  javascript에서의 ReduxToolkit 사용 예제 : https://lee-yo-han.github.io/redux/redux-toolkit-사용해보기

                                                                                                                                                                                                  +Rain Sugar BangBang Front-End

                                                                                                                                                                                                  Rain Sugar BangBang

                                                                                                                                                                                                  \ No newline at end of file +

                                                                                                                                                                                                  참고 : https://redux-toolkit.js.org/usage/usage-with-typescript

                                                                                                                                                                                                  "redux" 카테고리의 다른 글

                                                                                                                                                                                                    COMMENTS

                                                                                                                                                                                                    \ No newline at end of file diff --git a/redux-usage.html b/redux-usage.html index 228257b5..7bf960b5 100644 --- a/redux-usage.html +++ b/redux-usage.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                                                    Rain Sugar BangBang

                                                                                                                                                                                                    redux

                                                                                                                                                                                                    redux 사용해보기

                                                                                                                                                                                                    2023-03-04

                                                                                                                                                                                                    React에서 Redux 사용해보기

                                                                                                                                                                                                    +Rain Sugar BangBang Front-End

                                                                                                                                                                                                    Rain Sugar BangBang

                                                                                                                                                                                                    redux

                                                                                                                                                                                                    redux 사용해보기

                                                                                                                                                                                                    2023-03-04

                                                                                                                                                                                                    React에서 Redux 사용해보기

                                                                                                                                                                                                    리덕스 툴킷 x

                                                                                                                                                                                                    Redux 설치

                                                                                                                                                                                                    yarn add redux react-redux
                                                                                                                                                                                                    @@ -111,4 +111,4 @@

                                                                                                                                                                                                    컴포넌트에서의 사용

                                                                                                                                                                                                    ) } -

                                                                                                                                                                                                    참조 : https://ko.redux.js.org/

                                                                                                                                                                                                    "redux" 카테고리의 다른 글

                                                                                                                                                                                                      COMMENTS

                                                                                                                                                                                                      \ No newline at end of file +

                                                                                                                                                                                                      참조 : https://ko.redux.js.org/

                                                                                                                                                                                                      "redux" 카테고리의 다른 글

                                                                                                                                                                                                        COMMENTS

                                                                                                                                                                                                        \ No newline at end of file diff --git a/redux.html b/redux.html index e25aa65f..beb7cc9d 100644 --- a/redux.html +++ b/redux.html @@ -1 +1 @@ -Rain Sugar BangBang Front-End \ No newline at end of file +Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/search.html b/search.html index ed3113a3..2fb3de45 100644 --- a/search.html +++ b/search.html @@ -1 +1 @@ -Rain Sugar BangBang Front-End \ No newline at end of file +Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/cs-sw-test.html b/sw-test.html similarity index 82% rename from cs-sw-test.html rename to sw-test.html index 9a3c46b0..df8414ba 100644 --- a/cs-sw-test.html +++ b/sw-test.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                                                        Rain Sugar BangBang

                                                                                                                                                                                                        cs

                                                                                                                                                                                                        정보처리기사 테스트

                                                                                                                                                                                                        2023-12-27

                                                                                                                                                                                                        TEST

                                                                                                                                                                                                        +Rain Sugar BangBang Front-End

                                                                                                                                                                                                        Rain Sugar BangBang

                                                                                                                                                                                                        정보처리기사

                                                                                                                                                                                                        정보처리기사 테스트

                                                                                                                                                                                                        2023-12-27

                                                                                                                                                                                                        TEST

                                                                                                                                                                                                        TEST

                                                                                                                                                                                                        TEST

                                                                                                                                                                                                        TEST

                                                                                                                                                                                                        @@ -296,4 +296,4 @@

                                                                                                                                                                                                        TypeScript에서 더 큰 타입 구조를 갖는 변수에 작은 타입 구 // 에러가 나지 않는 방식. sum의 구조가 더 크다고 볼 수 있음 sum = add; -

                                                                                                                                                                                                        참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/

                                                                                                                                                                                                        "cs" 카테고리의 다른 글

                                                                                                                                                                                                          COMMENTS

                                                                                                                                                                                                          \ No newline at end of file +

                                                                                                                                                                                                          참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/

                                                                                                                                                                                                          "sw" 카테고리의 다른 글

                                                                                                                                                                                                            COMMENTS

                                                                                                                                                                                                            \ No newline at end of file diff --git a/sw.html b/sw.html new file mode 100644 index 00000000..9af79eee --- /dev/null +++ b/sw.html @@ -0,0 +1 @@ +Rain Sugar BangBang Front-End \ No newline at end of file diff --git a/typescript-syntax.html b/typescript-syntax.html index f15855f4..8b4299f4 100644 --- a/typescript-syntax.html +++ b/typescript-syntax.html @@ -1,4 +1,4 @@ -Rain Sugar BangBang Front-End

                                                                                                                                                                                                            Rain Sugar BangBang

                                                                                                                                                                                                            typescript

                                                                                                                                                                                                            Typescript 사용을 위한 기본적인 문법 정리

                                                                                                                                                                                                            2023-03-02

                                                                                                                                                                                                            기본 TypeScript 타입 선언

                                                                                                                                                                                                            +Rain Sugar BangBang Front-End

                                                                                                                                                                                                            Rain Sugar BangBang

                                                                                                                                                                                                            typescript

                                                                                                                                                                                                            Typescript 사용을 위한 기본적인 문법 정리

                                                                                                                                                                                                            2023-03-02

                                                                                                                                                                                                            기본 TypeScript 타입 선언

                                                                                                                                                                                                            
                                                                                                                                                                                                             // 문자열
                                                                                                                                                                                                             let str: string = "hello";
                                                                                                                                                                                                            @@ -291,4 +291,4 @@ 

                                                                                                                                                                                                            TypeScript에서 더 큰 타입 구조를 갖는 변수에 작은 타입 구 // 에러가 나지 않는 방식. sum의 구조가 더 크다고 볼 수 있음 sum = add;

                                                                                                                                                                                                            -

                                                                                                                                                                                                            참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/

                                                                                                                                                                                                            "typescript" 카테고리의 다른 글

                                                                                                                                                                                                              COMMENTS

                                                                                                                                                                                                              \ No newline at end of file +

                                                                                                                                                                                                              참조 : https://yeomkyeorae.github.io/typesciprt/basic_typescript/

                                                                                                                                                                                                              "typescript" 카테고리의 다른 글

                                                                                                                                                                                                                COMMENTS

                                                                                                                                                                                                                \ No newline at end of file diff --git a/typescript.html b/typescript.html index a7622709..c4201b14 100644 --- a/typescript.html +++ b/typescript.html @@ -1 +1 @@ -Rain Sugar BangBang Front-End \ No newline at end of file +Rain Sugar BangBang Front-End \ No newline at end of file