From e482a2a18ce05d646898c0288fbce875000a708e Mon Sep 17 00:00:00 2001 From: Conan Kunhwan Ahn Date: Wed, 7 Dec 2022 21:45:44 +0900 Subject: [PATCH 1/8] =?UTF-8?q?AWS=20Frontend=202022=20-=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EB=9E=98=EB=A8=B8=EC=8A=A4=20=ED=94=84?= =?UTF-8?q?=EB=A1=A0=ED=8A=B8=EC=97=94=EB=93=9C=20=EC=95=84=ED=82=A4?= =?UTF-8?q?=ED=85=8D=EC=B2=98=20=EB=B3=80=EC=B2=9C=EC=82=AC=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 폴더명 변경 * 발표 내용 정리 --- .../01_web_deploy_on_aws.md | 0 .../FE-Meetup__static-website-hosting.pdf | Bin .../00_index.md | 0 ...0_\353\263\200\354\262\234\354\202\254.md" | 217 ++++++++++++++++++ 4 files changed, 217 insertions(+) rename Amazon_AWS/{2019-02-10_aws_frontend => 19-02-10_aws_frontend}/01_web_deploy_on_aws.md (100%) rename Amazon_AWS/{2019-02-10_aws_frontend => 19-02-10_aws_frontend}/FE-Meetup__static-website-hosting.pdf (100%) rename Amazon_AWS/{2020-10-13_aws_eks_web => 20-10-13_aws_eks_web}/00_index.md (100%) create mode 100644 "Amazon_AWS/22-12-07_aws_frontend/01_\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244_\354\225\204\355\202\244\355\205\215\354\262\230_\353\263\200\354\262\234\354\202\254.md" diff --git a/Amazon_AWS/2019-02-10_aws_frontend/01_web_deploy_on_aws.md b/Amazon_AWS/19-02-10_aws_frontend/01_web_deploy_on_aws.md similarity index 100% rename from Amazon_AWS/2019-02-10_aws_frontend/01_web_deploy_on_aws.md rename to Amazon_AWS/19-02-10_aws_frontend/01_web_deploy_on_aws.md diff --git a/Amazon_AWS/2019-02-10_aws_frontend/FE-Meetup__static-website-hosting.pdf b/Amazon_AWS/19-02-10_aws_frontend/FE-Meetup__static-website-hosting.pdf similarity index 100% rename from Amazon_AWS/2019-02-10_aws_frontend/FE-Meetup__static-website-hosting.pdf rename to Amazon_AWS/19-02-10_aws_frontend/FE-Meetup__static-website-hosting.pdf diff --git a/Amazon_AWS/2020-10-13_aws_eks_web/00_index.md b/Amazon_AWS/20-10-13_aws_eks_web/00_index.md similarity index 100% rename from Amazon_AWS/2020-10-13_aws_eks_web/00_index.md rename to Amazon_AWS/20-10-13_aws_eks_web/00_index.md diff --git "a/Amazon_AWS/22-12-07_aws_frontend/01_\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244_\354\225\204\355\202\244\355\205\215\354\262\230_\353\263\200\354\262\234\354\202\254.md" "b/Amazon_AWS/22-12-07_aws_frontend/01_\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244_\354\225\204\355\202\244\355\205\215\354\262\230_\353\263\200\354\262\234\354\202\254.md" new file mode 100644 index 0000000..c940cb4 --- /dev/null +++ "b/Amazon_AWS/22-12-07_aws_frontend/01_\355\224\204\353\241\234\352\267\270\353\236\230\353\250\270\354\212\244_\354\225\204\355\202\244\355\205\215\354\262\230_\353\263\200\354\262\234\354\202\254.md" @@ -0,0 +1,217 @@ +# 프로그래머스 프론트엔드 아키텍처 변천사: 좋은 개발 경험을 찾아서 +- 김은수 - 프로그래머스 +- [발표자료](https://www.slideshare.net/PeterEunsuKim/ss-254802834) + +## 프로그래머스 프론트엔드 아키텍처(~2021) +- Ruby on Rails +- Vue + Webpack + +### 겪은 문제들 +- Vue 코드만 수정했는데 배포하는데 너무 오래 걸려요 +- 서로 다른 팀이 하나의 package.json을 공유해서 의존성 관리가 힘들어요 +- Rails Webpacker에 강한 의존성이 생겼어요 +- => 결론적으로 개발 경험이 안 좋아요 + +## 프론트엔드 개발자가 원하는 것 +- 빠르게 개발하고 배포하는 것 +- 의존성 관리도 팀별로 알아서 척척 +- Vue말고 React도 쓰고 싶다 + +> 그래서 우리는 프론트엔드 아키텍처를 개선하기로 했습니다 + +## New 프론트엔드 아키텍처 설계 + +### DO +- 프론트엔드를 Rails 애플리케이션과 독립적으로 개발/빌드/배포 가능 +- 프론트엔드 코드베이스는 모노레포로 관리 + - 전사 공통 패키지 외에는 팀에서 자유롭게 의존성 도입 +- 기존 아키텍처와 새로운 아키텍처가 공존하고, 점진적(Progressive)으로 마이그레이션 가능 + +### DON'T +- 아키텍처를 바꾸느라 제품 개발 프로세스가 멈추면 안됨 + +## 선행 조사 +- Airbnb 사례 - [Hypernova](https://github.com/airbnb/hypernova) +- 오늘의집 사례 - [오늘의집 MSA Phase 1. 프론트엔드 분리작업](https://www.bucketplace.com/post/2021-12-03-%EC%98%A4%EB%8A%98%EC%9D%98%EC%A7%91-msa-phase-1-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%B6%84%EB%A6%AC%EC%9E%91%EC%97%85/) + +### Airbnb 사례 - Hypernova +- 클라이언트 코드가 서버 코드로부터 분리될 수 있음 +- 점진적으로 적용 가능함 +- 실제로 구현된 사례를 확인함으로써 아키텍처 설계에 대한 힌트를 얻게 되었다 + +#### Server +```JavaScript +var hypernova = require('hypernova/server'); + +hypernova({ + devMode: true, + getComponent(name) { + if (name === 'MyComponent.js') { + return require('./app/assets/javascripts/MyComponent.js);'); + } + + return null; + }, + port: 3030, +}); +``` + +#### MyComponent.js +```JavaScript +const React = require('react'); +const renderReact = require('hypernova-react').renderReact; + +function MyComponent(props) { + return
Hello, {props.name}!
; +} + +module.exports = renderReact('MyComponent.js', MyComponent); +``` + +### New 프로그래머스 프론트엔드 아키텍처 - Core Concept +- 프론트엔드 코드를 저장할 모노 레포지토리 +- 프론트엔드 애플리케이션 manifest를 Rails 템플릿에 임베딩 + - Webpack Manifest +- Vue / React / Anything in Rails + +#### Webpack Manifest +- Webpack으로 빌드된 애플리케이션을 포함하는 코드 +- 개발자가 작성한 소스 코드 +- 작성한 소스 코드가 의존하는 라이브러리 및 벤더(Vendor)코드 +- **Webpack runtime 및 manifest** + - 어떤 컴포넌트를 어떤 방식으로 어떻게 로딩을 해야 하는지와 같은 내용이 정리되어 있음 + +#### Webpack Manifest Plugin +- Webpack manifest를 추출할 수 있게 해주는 플러그인 +- webpack-manifest-plugin => `manifest.json` 파일을 생성함 + +##### webpack.config.ts +```TypeScript +import { WebpackManifestPlugin } from 'webpack-manifest-plugin'; + +const productionConfig: Configuration = { + mode: 'production', + output: { filename: '[contenthash].js' }, + module: { /*...*/ }, + plugins: [ + new WebpackManifestPlugin(), + ], +}; +``` + +##### dist/manifest.json +```json +{ + "main.js": "/hashed-name-1.js", + "175.css": "/175.hashed-name-2.css", + "175.css.map": "/175.hashed-name-2.css.map", + "js": "/hashed-name-3.js", + "js.map": "/hashed-name-3.js.map", + "index.html": "/index.html" +} +``` + +### 개선된 아키텍처의 장점 - Scalability +- 기존 Rails 코드베이스의 수정 없이 새로운 웹 애플리케이션 개발 및 배포가 가능 + +### 개선된 아키텍처의 장점 - Independency +- 애플리케이션의 의존성을 독립적으로 관리가 가능 + +### 개선된 아키텍처의 불편한 점 +- 각 애플리케이션에서 의존하는 코드가 새로 릴리즈되면 해당 사항을 반영하기 위해 모든 앱을 다시 배포해야 함 + +> 앱을 다시 배포하지 않고 의존하는 코드의 변경사항을 런타임에 바로 적용할 수 없을까? => Webpack5 Module Federation + +## Webpack5 Module Federation - Core Concept +> NOTE: Docker의 Container가 아니고, 용어임 +- 빌드된 애플리케이션 = 컨테이너 + - 호스트(Host) 컨테이너: 외부에 노출된 모듈을 불러와 사용 + - 리모트(Remote) 컨테이너: 모듈을 외부로 노출(expose) +- Webpack 5버전부터 포함된 Container Plugin을 사용하여 적용 가능 + - Container Plugin을 추사오하한 Mudle Federation Plugin을 사용하면 더 쉽게 적용 가능 + +### Host 컨테이너의 Webpack Config +```TypeScript +import { container } from 'webpack'; + +const config: Configuration = { + // ...생략 + plugins: [ + new container.ModuleFederationPlugin({ + remotes: { + remoteApp: `remoteApp@${process.env.APP_URL}/remoteEntry.js`, + } + }), + ], +}; +``` + +### Remote 컨테이너의 Webpack Config +```TypeScript +import { container } from 'webpack'; + +const config: Configuration = { + // ...생략 + plugins: [ + new container.ModuleFederationPlugin({ + name: 'remoteApp', + filename: 'remoteEntry.js', + exposes: { + './Header': './src/components/Header.tsx', + } + }), + ], +}; +``` + +### Mudule Federation 사용 예시 +```JavaScript +import { Suspense, lazy } from 'react'; +import { LoadingSpinner } from '@/components/LoadingSpinner'; + +const Header = lazy(() => import('remoteApp/Header')); + +function CustomHeader() { + return ( + }> +
+ + ); +} + +export default CustomHeader; +``` + +> 그래서 우리는 얼마나 빠르게 제품을 개발하고 배포할 수 있게 되었을까요? +> 빌드시간: 30~40분 => 5분 +> 릴리즈 횟수: 46회 => 173회 +> Closed Pull Request: 72 PRs / month => 175 PRs / month + +## Next Steps +- Rails 번들러 마이그레이션 +- Rails에서 번들러 의존성 제거 +- 개발 경험 개선 + +### Rails 번들러 마이그레이션 +- Rails Webpacker를 Shakapacker로 마이그레이션 하여 Webpack 5버전을 사용할 뿐만 아니라 babel 외의 빌드 도구(swc, esbuild)도 사용 가능 + +### Rails에서 번들러 의존성 제거 +- Rails로부터 번들러(Webpacker) 의존성을 분리함으로써 Rails는 API 서버처럼 동작 + +### 개발 경험 개선 +- Remote module의 type checking +- 앱 간의 커뮤니케이션 컨벤션 정의 +- Best Practice 탐구 + +> 프로그래머스의 프론트엔드 개발 경험을 개선하기 위한 모험은 계속됩니다. + +## Recap +- Rails와 Vue가 함께 있는 레포지토리에서 프론트엔드 개발 경험이 좋지 않음 +- 프론트엔드 코드베이스를 Rails 코드와 분리시켜 독립적으로 개발/빌드/배포할 수 있고 점진적으로 마이그레이션 가능한 아키텍처를 설계 + - Airbnb의 Hypernova 사례를 바탕으로 + - 신규 프론트엔드 애플리케이션의 Webpack manifest를 Rails 템플릿에 임베딩 + - 신규 프론트엔드 애플리케이션 간의 Module Federation을 통한 Runtime Integration +- 개발 경험을 개선하기 위한 노력이 여전히 진행 중 + - Rails 번들러 마이그레이션 + - Rails에서 번들러 의존성 제거 + - 신규 프론트엔드 코드베이스 개발 경험 개선 From 6f22fe13a9ad4f1d42e2a30174824dcb7300620b Mon Sep 17 00:00:00 2001 From: Conan Kunhwan Ahn Date: Fri, 9 Dec 2022 23:06:06 +0900 Subject: [PATCH 2/8] [GDG] Devfest 2022 (#20) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * rename folder names * 리눅스 커널 기여 경험기 문서 추가 * 서비스 종료와 이직 경험 문서 추가 * 스타트업 8년간의 회고 문서 추가 * 퇴사 리뷰 문서 추가 --- .../01_Animation/index.html | 0 .../01_Animation/test.html | 0 .../mysw101/index.html | 0 .../mysw101/manifest.json | 0 GDGKR/{2016_July => 16-07_July}/mysw101/sw.js | 0 .../Big_Query_Usecase.md | 0 .../Google_Cloud.md | 0 .../Translation_Hobby.md | 0 .../01_foriegn_engineers.md | 0 .../02_I_am_not_a_developer.md | 0 .../03_jenkins_as_code.md | 0 .../04_aws_lambda_reactjs.md | 0 .../05_andorid_starter_kit.md | 0 .../06_dancing_programmer.md | 0 .../07_study_collect.md | 0 .../08_realm_sync.md | 0 .../00_index.md | 0 .../01_keynote.md | 0 .../02_session_1.md | 0 .../03_session_2.md | 0 .../04_session_3.md | 0 .../05_session_4.md | 0 .../06_session_5.md | 0 .../07_session_6.md | 0 .../08_session_7.md | 0 .../09_session_8.md | 0 .../00_keynote.md | 0 .../01_google_machine_learing.md | 0 .../02_auto_ml_auto_draw.md | 0 .../03_android_things.md | 0 .../04_it_trend_noops_big_data_ml.md | 0 .../05_ready_action.md | 0 .../01_angular_react_view.md | 0 .../02_react_in_enterprise.md | 0 .../03_angular_basic.md | 0 .../04_experience_vuejs.md | 0 .../01_typescript_clean_architecture.md | 0 .../02_mvc_mvvm.md | 0 .../03_d3_basic.md | 0 .../04_android_things.md | 0 .../05_github_cloudflare.md | 0 .../00_google_cloud_next_extended.md | 0 .../01_free_tier_gcp_serverless.md | 0 .../02_gcp_key_updates.md | 0 .../03_kubernetes_on_gcp.md | 0 .../01_progressive_web_apps.md | 0 .../02_scroll_snap.md | 0 .../scroll_snap_exmaples/example_1.html | 0 .../scroll_snap_exmaples/example_2.html | 0 .../00_intro.md | 0 .../01_chrome_devtools.md | 0 .../02_about_spa_status_management.md | 0 .../03_msa_istio.md | 0 .../04_ strict_javascript.md | 0 .../05_chromium_blink.md | 0 .../01_frontend_level_up_with_angular.md | 0 .../02_history_of_min.md | 0 .../03_guide_for_frontend_traveler.md | 0 .../01_what_is_new_in_web.md | 0 .../02_introduce_flutter.md | 0 .../03_google_seach_js_pages.md | 0 .../01_puppeteer.md | 0 .../02_portals.md | 0 .../03_new_capabilities_for_the_web.md | 0 .../04_web_assembly_101.md | 0 .../05_going_big_pwa.md | 0 .../06_deep_in_lighthouse_audits.md | 0 .../sample/crawling.js | 0 .../sample/package-lock.json | 0 .../sample/package.json | 0 .../sample/pdf.js | 0 .../sample/screenshot.js | 0 .../00_keynote.md | 0 .../01_functional_programming.md | 0 .../02_web_gpu_is_coming.md | 0 .../03_atomic_design_pattern.md | 0 .../04_web_service_msa.md | 0 .../05_vertical_align.md | 0 .../00_keynote.md | 0 .../01_visbug_opensource.md | 0 .../02_main_thread_with_code.md | 0 .../03_http3_performance.md | 0 .../04_modern_web_assembly.md | 0 .../05_video_editor_on_web.md | 0 .../00_keynote.md | 0 .../01_what's_new_in_dev_tools.md | 0 .../02_extending_css_with_houdini.md | 0 .../03_transitioning_to_modern_javascript.md | 0 .../04_structured_data_for_developers.md | 0 ...ng_the_web_more_visual_with_web_stories.md | 0 .../06_the_web_ahead.md | 0 .../07_state_of_speed_tooling.md | 0 .../08_fixing_common_web_vitals_issues.md | 0 ..._patterns_optimized_for_core_web_vitals.md | 0 ...exploring_the_future_of_core_web_vitals.md | 0 .../11_beyond_fast.md | 0 .../12_3x3_seo_tips.md | 0 GDGKR/22-12-09_dev_fest/01_linux_kernel.md | 114 ++++++++++++++++++ GDGKR/22-12-09_dev_fest/02_service_end.md | 84 +++++++++++++ GDGKR/22-12-09_dev_fest/03_8years_startup.md | 68 +++++++++++ GDGKR/22-12-09_dev_fest/04_winter_resign.md | 72 +++++++++++ 101 files changed, 338 insertions(+) rename GDGKR/{2016_July => 16-07_July}/01_Animation/index.html (100%) rename GDGKR/{2016_July => 16-07_July}/01_Animation/test.html (100%) rename GDGKR/{2016_July => 16-07_July}/mysw101/index.html (100%) rename GDGKR/{2016_July => 16-07_July}/mysw101/manifest.json (100%) rename GDGKR/{2016_July => 16-07_July}/mysw101/sw.js (100%) rename GDGKR/{2016_September => 16-09_September}/Big_Query_Usecase.md (100%) rename GDGKR/{2016_September => 16-09_September}/Google_Cloud.md (100%) rename GDGKR/{2016_September => 16-09_September}/Translation_Hobby.md (100%) rename GDGKR/{2016_December => 16-12_December}/01_foriegn_engineers.md (100%) rename GDGKR/{2016_December => 16-12_December}/02_I_am_not_a_developer.md (100%) rename GDGKR/{2016_December => 16-12_December}/03_jenkins_as_code.md (100%) rename GDGKR/{2016_December => 16-12_December}/04_aws_lambda_reactjs.md (100%) rename GDGKR/{2016_December => 16-12_December}/05_andorid_starter_kit.md (100%) rename GDGKR/{2016_December => 16-12_December}/06_dancing_programmer.md (100%) rename GDGKR/{2016_December => 16-12_December}/07_study_collect.md (100%) rename GDGKR/{2016_December => 16-12_December}/08_realm_sync.md (100%) rename GDGKR/{2017-04-23_pwa_roadshow => 17-04-23_pwa_roadshow}/00_index.md (100%) rename GDGKR/{2017-04-23_pwa_roadshow => 17-04-23_pwa_roadshow}/01_keynote.md (100%) rename GDGKR/{2017-04-23_pwa_roadshow => 17-04-23_pwa_roadshow}/02_session_1.md (100%) rename GDGKR/{2017-04-23_pwa_roadshow => 17-04-23_pwa_roadshow}/03_session_2.md (100%) rename GDGKR/{2017-04-23_pwa_roadshow => 17-04-23_pwa_roadshow}/04_session_3.md (100%) rename GDGKR/{2017-04-23_pwa_roadshow => 17-04-23_pwa_roadshow}/05_session_4.md (100%) rename GDGKR/{2017-04-23_pwa_roadshow => 17-04-23_pwa_roadshow}/06_session_5.md (100%) rename GDGKR/{2017-04-23_pwa_roadshow => 17-04-23_pwa_roadshow}/07_session_6.md (100%) rename GDGKR/{2017-04-23_pwa_roadshow => 17-04-23_pwa_roadshow}/08_session_7.md (100%) rename GDGKR/{2017-04-23_pwa_roadshow => 17-04-23_pwa_roadshow}/09_session_8.md (100%) rename GDGKR/{2017-07-02_google_io_extended => 17-07-02_google_io_extended}/00_keynote.md (100%) rename GDGKR/{2017-07-02_google_io_extended => 17-07-02_google_io_extended}/01_google_machine_learing.md (100%) rename GDGKR/{2017-07-02_google_io_extended => 17-07-02_google_io_extended}/02_auto_ml_auto_draw.md (100%) rename GDGKR/{2017-07-02_google_io_extended => 17-07-02_google_io_extended}/03_android_things.md (100%) rename GDGKR/{2017-07-02_google_io_extended => 17-07-02_google_io_extended}/04_it_trend_noops_big_data_ml.md (100%) rename GDGKR/{2017-07-02_google_io_extended => 17-07-02_google_io_extended}/05_ready_action.md (100%) rename GDGKR/{2017-08-27_frontend_story => 17-08-27_frontend_story}/01_angular_react_view.md (100%) rename GDGKR/{2017-08-27_frontend_story => 17-08-27_frontend_story}/02_react_in_enterprise.md (100%) rename GDGKR/{2017-08-27_frontend_story => 17-08-27_frontend_story}/03_angular_basic.md (100%) rename GDGKR/{2017-08-27_frontend_story => 17-08-27_frontend_story}/04_experience_vuejs.md (100%) rename GDGKR/{2017-11-19_dev_fest => 17-11-19_dev_fest}/01_typescript_clean_architecture.md (100%) rename GDGKR/{2017-11-19_dev_fest => 17-11-19_dev_fest}/02_mvc_mvvm.md (100%) rename GDGKR/{2017-11-19_dev_fest => 17-11-19_dev_fest}/03_d3_basic.md (100%) rename GDGKR/{2017-11-19_dev_fest => 17-11-19_dev_fest}/04_android_things.md (100%) rename GDGKR/{2017-11-19_dev_fest => 17-11-19_dev_fest}/05_github_cloudflare.md (100%) rename GDGKR/{2017_March => 17_March}/00_google_cloud_next_extended.md (100%) rename GDGKR/{2017_March => 17_March}/01_free_tier_gcp_serverless.md (100%) rename GDGKR/{2017_March => 17_March}/02_gcp_key_updates.md (100%) rename GDGKR/{2017_March => 17_March}/03_kubernetes_on_gcp.md (100%) rename GDGKR/{2018-08-20_chrome_dev_meetup_1 => 18-08-20_chrome_dev_meetup_1}/01_progressive_web_apps.md (100%) rename GDGKR/{2018-08-20_chrome_dev_meetup_1 => 18-08-20_chrome_dev_meetup_1}/02_scroll_snap.md (100%) rename GDGKR/{2018-08-20_chrome_dev_meetup_1 => 18-08-20_chrome_dev_meetup_1}/scroll_snap_exmaples/example_1.html (100%) rename GDGKR/{2018-08-20_chrome_dev_meetup_1 => 18-08-20_chrome_dev_meetup_1}/scroll_snap_exmaples/example_2.html (100%) rename GDGKR/{2018-11-10_dev_fest => 18-11-10_dev_fest}/00_intro.md (100%) rename GDGKR/{2018-11-10_dev_fest => 18-11-10_dev_fest}/01_chrome_devtools.md (100%) rename GDGKR/{2018-11-10_dev_fest => 18-11-10_dev_fest}/02_about_spa_status_management.md (100%) rename GDGKR/{2018-11-10_dev_fest => 18-11-10_dev_fest}/03_msa_istio.md (100%) rename GDGKR/{2018-11-10_dev_fest => 18-11-10_dev_fest}/04_ strict_javascript.md (100%) rename GDGKR/{2018-11-10_dev_fest => 18-11-10_dev_fest}/05_chromium_blink.md (100%) rename GDGKR/{2018-11-28_devfest_webtech_2018 => 18-11-28_devfest_webtech_2018}/01_frontend_level_up_with_angular.md (100%) rename GDGKR/{2018-11-28_devfest_webtech_2018 => 18-11-28_devfest_webtech_2018}/02_history_of_min.md (100%) rename GDGKR/{2018-11-28_devfest_webtech_2018 => 18-11-28_devfest_webtech_2018}/03_guide_for_frontend_traveler.md (100%) rename GDGKR/{2019-06-30_google_io_extended => 19-06-30_google_io_extended}/01_what_is_new_in_web.md (100%) rename GDGKR/{2019-06-30_google_io_extended => 19-06-30_google_io_extended}/02_introduce_flutter.md (100%) rename GDGKR/{2019-06-30_google_io_extended => 19-06-30_google_io_extended}/03_google_seach_js_pages.md (100%) rename GDGKR/{2019-07-13_io_extended_web_tech => 19-07-13_io_extended_web_tech}/01_puppeteer.md (100%) rename GDGKR/{2019-07-13_io_extended_web_tech => 19-07-13_io_extended_web_tech}/02_portals.md (100%) rename GDGKR/{2019-07-13_io_extended_web_tech => 19-07-13_io_extended_web_tech}/03_new_capabilities_for_the_web.md (100%) rename GDGKR/{2019-07-13_io_extended_web_tech => 19-07-13_io_extended_web_tech}/04_web_assembly_101.md (100%) rename GDGKR/{2019-07-13_io_extended_web_tech => 19-07-13_io_extended_web_tech}/05_going_big_pwa.md (100%) rename GDGKR/{2019-07-13_io_extended_web_tech => 19-07-13_io_extended_web_tech}/06_deep_in_lighthouse_audits.md (100%) rename GDGKR/{2019-07-13_io_extended_web_tech => 19-07-13_io_extended_web_tech}/sample/crawling.js (100%) rename GDGKR/{2019-07-13_io_extended_web_tech => 19-07-13_io_extended_web_tech}/sample/package-lock.json (100%) rename GDGKR/{2019-07-13_io_extended_web_tech => 19-07-13_io_extended_web_tech}/sample/package.json (100%) rename GDGKR/{2019-07-13_io_extended_web_tech => 19-07-13_io_extended_web_tech}/sample/pdf.js (100%) rename GDGKR/{2019-07-13_io_extended_web_tech => 19-07-13_io_extended_web_tech}/sample/screenshot.js (100%) rename GDGKR/{2019-10-20_devfest_seoul => 19-10-20_devfest_seoul}/00_keynote.md (100%) rename GDGKR/{2019-10-20_devfest_seoul => 19-10-20_devfest_seoul}/01_functional_programming.md (100%) rename GDGKR/{2019-10-20_devfest_seoul => 19-10-20_devfest_seoul}/02_web_gpu_is_coming.md (100%) rename GDGKR/{2019-10-20_devfest_seoul => 19-10-20_devfest_seoul}/03_atomic_design_pattern.md (100%) rename GDGKR/{2019-10-20_devfest_seoul => 19-10-20_devfest_seoul}/04_web_service_msa.md (100%) rename GDGKR/{2019-10-20_devfest_seoul => 19-10-20_devfest_seoul}/05_vertical_align.md (100%) rename GDGKR/{2019-11-23_devfest_webtech_2019 => 19-11-23_devfest_webtech_2019}/00_keynote.md (100%) rename GDGKR/{2019-11-23_devfest_webtech_2019 => 19-11-23_devfest_webtech_2019}/01_visbug_opensource.md (100%) rename GDGKR/{2019-11-23_devfest_webtech_2019 => 19-11-23_devfest_webtech_2019}/02_main_thread_with_code.md (100%) rename GDGKR/{2019-11-23_devfest_webtech_2019 => 19-11-23_devfest_webtech_2019}/03_http3_performance.md (100%) rename GDGKR/{2019-11-23_devfest_webtech_2019 => 19-11-23_devfest_webtech_2019}/04_modern_web_assembly.md (100%) rename GDGKR/{2019-11-23_devfest_webtech_2019 => 19-11-23_devfest_webtech_2019}/05_video_editor_on_web.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/00_keynote.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/01_what's_new_in_dev_tools.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/02_extending_css_with_houdini.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/03_transitioning_to_modern_javascript.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/04_structured_data_for_developers.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/05_making_the_web_more_visual_with_web_stories.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/06_the_web_ahead.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/07_state_of_speed_tooling.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/08_fixing_common_web_vitals_issues.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/09_improve_ux_patterns_optimized_for_core_web_vitals.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/10_exploring_the_future_of_core_web_vitals.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/11_beyond_fast.md (100%) rename GDGKR/{2020-12-11_chrome_dev_summit_2020 => 20-12-11_chrome_dev_summit_2020}/12_3x3_seo_tips.md (100%) create mode 100644 GDGKR/22-12-09_dev_fest/01_linux_kernel.md create mode 100644 GDGKR/22-12-09_dev_fest/02_service_end.md create mode 100644 GDGKR/22-12-09_dev_fest/03_8years_startup.md create mode 100644 GDGKR/22-12-09_dev_fest/04_winter_resign.md diff --git a/GDGKR/2016_July/01_Animation/index.html b/GDGKR/16-07_July/01_Animation/index.html similarity index 100% rename from GDGKR/2016_July/01_Animation/index.html rename to GDGKR/16-07_July/01_Animation/index.html diff --git a/GDGKR/2016_July/01_Animation/test.html b/GDGKR/16-07_July/01_Animation/test.html similarity index 100% rename from GDGKR/2016_July/01_Animation/test.html rename to GDGKR/16-07_July/01_Animation/test.html diff --git a/GDGKR/2016_July/mysw101/index.html b/GDGKR/16-07_July/mysw101/index.html similarity index 100% rename from GDGKR/2016_July/mysw101/index.html rename to GDGKR/16-07_July/mysw101/index.html diff --git a/GDGKR/2016_July/mysw101/manifest.json b/GDGKR/16-07_July/mysw101/manifest.json similarity index 100% rename from GDGKR/2016_July/mysw101/manifest.json rename to GDGKR/16-07_July/mysw101/manifest.json diff --git a/GDGKR/2016_July/mysw101/sw.js b/GDGKR/16-07_July/mysw101/sw.js similarity index 100% rename from GDGKR/2016_July/mysw101/sw.js rename to GDGKR/16-07_July/mysw101/sw.js diff --git a/GDGKR/2016_September/Big_Query_Usecase.md b/GDGKR/16-09_September/Big_Query_Usecase.md similarity index 100% rename from GDGKR/2016_September/Big_Query_Usecase.md rename to GDGKR/16-09_September/Big_Query_Usecase.md diff --git a/GDGKR/2016_September/Google_Cloud.md b/GDGKR/16-09_September/Google_Cloud.md similarity index 100% rename from GDGKR/2016_September/Google_Cloud.md rename to GDGKR/16-09_September/Google_Cloud.md diff --git a/GDGKR/2016_September/Translation_Hobby.md b/GDGKR/16-09_September/Translation_Hobby.md similarity index 100% rename from GDGKR/2016_September/Translation_Hobby.md rename to GDGKR/16-09_September/Translation_Hobby.md diff --git a/GDGKR/2016_December/01_foriegn_engineers.md b/GDGKR/16-12_December/01_foriegn_engineers.md similarity index 100% rename from GDGKR/2016_December/01_foriegn_engineers.md rename to GDGKR/16-12_December/01_foriegn_engineers.md diff --git a/GDGKR/2016_December/02_I_am_not_a_developer.md b/GDGKR/16-12_December/02_I_am_not_a_developer.md similarity index 100% rename from GDGKR/2016_December/02_I_am_not_a_developer.md rename to GDGKR/16-12_December/02_I_am_not_a_developer.md diff --git a/GDGKR/2016_December/03_jenkins_as_code.md b/GDGKR/16-12_December/03_jenkins_as_code.md similarity index 100% rename from GDGKR/2016_December/03_jenkins_as_code.md rename to GDGKR/16-12_December/03_jenkins_as_code.md diff --git a/GDGKR/2016_December/04_aws_lambda_reactjs.md b/GDGKR/16-12_December/04_aws_lambda_reactjs.md similarity index 100% rename from GDGKR/2016_December/04_aws_lambda_reactjs.md rename to GDGKR/16-12_December/04_aws_lambda_reactjs.md diff --git a/GDGKR/2016_December/05_andorid_starter_kit.md b/GDGKR/16-12_December/05_andorid_starter_kit.md similarity index 100% rename from GDGKR/2016_December/05_andorid_starter_kit.md rename to GDGKR/16-12_December/05_andorid_starter_kit.md diff --git a/GDGKR/2016_December/06_dancing_programmer.md b/GDGKR/16-12_December/06_dancing_programmer.md similarity index 100% rename from GDGKR/2016_December/06_dancing_programmer.md rename to GDGKR/16-12_December/06_dancing_programmer.md diff --git a/GDGKR/2016_December/07_study_collect.md b/GDGKR/16-12_December/07_study_collect.md similarity index 100% rename from GDGKR/2016_December/07_study_collect.md rename to GDGKR/16-12_December/07_study_collect.md diff --git a/GDGKR/2016_December/08_realm_sync.md b/GDGKR/16-12_December/08_realm_sync.md similarity index 100% rename from GDGKR/2016_December/08_realm_sync.md rename to GDGKR/16-12_December/08_realm_sync.md diff --git a/GDGKR/2017-04-23_pwa_roadshow/00_index.md b/GDGKR/17-04-23_pwa_roadshow/00_index.md similarity index 100% rename from GDGKR/2017-04-23_pwa_roadshow/00_index.md rename to GDGKR/17-04-23_pwa_roadshow/00_index.md diff --git a/GDGKR/2017-04-23_pwa_roadshow/01_keynote.md b/GDGKR/17-04-23_pwa_roadshow/01_keynote.md similarity index 100% rename from GDGKR/2017-04-23_pwa_roadshow/01_keynote.md rename to GDGKR/17-04-23_pwa_roadshow/01_keynote.md diff --git a/GDGKR/2017-04-23_pwa_roadshow/02_session_1.md b/GDGKR/17-04-23_pwa_roadshow/02_session_1.md similarity index 100% rename from GDGKR/2017-04-23_pwa_roadshow/02_session_1.md rename to GDGKR/17-04-23_pwa_roadshow/02_session_1.md diff --git a/GDGKR/2017-04-23_pwa_roadshow/03_session_2.md b/GDGKR/17-04-23_pwa_roadshow/03_session_2.md similarity index 100% rename from GDGKR/2017-04-23_pwa_roadshow/03_session_2.md rename to GDGKR/17-04-23_pwa_roadshow/03_session_2.md diff --git a/GDGKR/2017-04-23_pwa_roadshow/04_session_3.md b/GDGKR/17-04-23_pwa_roadshow/04_session_3.md similarity index 100% rename from GDGKR/2017-04-23_pwa_roadshow/04_session_3.md rename to GDGKR/17-04-23_pwa_roadshow/04_session_3.md diff --git a/GDGKR/2017-04-23_pwa_roadshow/05_session_4.md b/GDGKR/17-04-23_pwa_roadshow/05_session_4.md similarity index 100% rename from GDGKR/2017-04-23_pwa_roadshow/05_session_4.md rename to GDGKR/17-04-23_pwa_roadshow/05_session_4.md diff --git a/GDGKR/2017-04-23_pwa_roadshow/06_session_5.md b/GDGKR/17-04-23_pwa_roadshow/06_session_5.md similarity index 100% rename from GDGKR/2017-04-23_pwa_roadshow/06_session_5.md rename to GDGKR/17-04-23_pwa_roadshow/06_session_5.md diff --git a/GDGKR/2017-04-23_pwa_roadshow/07_session_6.md b/GDGKR/17-04-23_pwa_roadshow/07_session_6.md similarity index 100% rename from GDGKR/2017-04-23_pwa_roadshow/07_session_6.md rename to GDGKR/17-04-23_pwa_roadshow/07_session_6.md diff --git a/GDGKR/2017-04-23_pwa_roadshow/08_session_7.md b/GDGKR/17-04-23_pwa_roadshow/08_session_7.md similarity index 100% rename from GDGKR/2017-04-23_pwa_roadshow/08_session_7.md rename to GDGKR/17-04-23_pwa_roadshow/08_session_7.md diff --git a/GDGKR/2017-04-23_pwa_roadshow/09_session_8.md b/GDGKR/17-04-23_pwa_roadshow/09_session_8.md similarity index 100% rename from GDGKR/2017-04-23_pwa_roadshow/09_session_8.md rename to GDGKR/17-04-23_pwa_roadshow/09_session_8.md diff --git a/GDGKR/2017-07-02_google_io_extended/00_keynote.md b/GDGKR/17-07-02_google_io_extended/00_keynote.md similarity index 100% rename from GDGKR/2017-07-02_google_io_extended/00_keynote.md rename to GDGKR/17-07-02_google_io_extended/00_keynote.md diff --git a/GDGKR/2017-07-02_google_io_extended/01_google_machine_learing.md b/GDGKR/17-07-02_google_io_extended/01_google_machine_learing.md similarity index 100% rename from GDGKR/2017-07-02_google_io_extended/01_google_machine_learing.md rename to GDGKR/17-07-02_google_io_extended/01_google_machine_learing.md diff --git a/GDGKR/2017-07-02_google_io_extended/02_auto_ml_auto_draw.md b/GDGKR/17-07-02_google_io_extended/02_auto_ml_auto_draw.md similarity index 100% rename from GDGKR/2017-07-02_google_io_extended/02_auto_ml_auto_draw.md rename to GDGKR/17-07-02_google_io_extended/02_auto_ml_auto_draw.md diff --git a/GDGKR/2017-07-02_google_io_extended/03_android_things.md b/GDGKR/17-07-02_google_io_extended/03_android_things.md similarity index 100% rename from GDGKR/2017-07-02_google_io_extended/03_android_things.md rename to GDGKR/17-07-02_google_io_extended/03_android_things.md diff --git a/GDGKR/2017-07-02_google_io_extended/04_it_trend_noops_big_data_ml.md b/GDGKR/17-07-02_google_io_extended/04_it_trend_noops_big_data_ml.md similarity index 100% rename from GDGKR/2017-07-02_google_io_extended/04_it_trend_noops_big_data_ml.md rename to GDGKR/17-07-02_google_io_extended/04_it_trend_noops_big_data_ml.md diff --git a/GDGKR/2017-07-02_google_io_extended/05_ready_action.md b/GDGKR/17-07-02_google_io_extended/05_ready_action.md similarity index 100% rename from GDGKR/2017-07-02_google_io_extended/05_ready_action.md rename to GDGKR/17-07-02_google_io_extended/05_ready_action.md diff --git a/GDGKR/2017-08-27_frontend_story/01_angular_react_view.md b/GDGKR/17-08-27_frontend_story/01_angular_react_view.md similarity index 100% rename from GDGKR/2017-08-27_frontend_story/01_angular_react_view.md rename to GDGKR/17-08-27_frontend_story/01_angular_react_view.md diff --git a/GDGKR/2017-08-27_frontend_story/02_react_in_enterprise.md b/GDGKR/17-08-27_frontend_story/02_react_in_enterprise.md similarity index 100% rename from GDGKR/2017-08-27_frontend_story/02_react_in_enterprise.md rename to GDGKR/17-08-27_frontend_story/02_react_in_enterprise.md diff --git a/GDGKR/2017-08-27_frontend_story/03_angular_basic.md b/GDGKR/17-08-27_frontend_story/03_angular_basic.md similarity index 100% rename from GDGKR/2017-08-27_frontend_story/03_angular_basic.md rename to GDGKR/17-08-27_frontend_story/03_angular_basic.md diff --git a/GDGKR/2017-08-27_frontend_story/04_experience_vuejs.md b/GDGKR/17-08-27_frontend_story/04_experience_vuejs.md similarity index 100% rename from GDGKR/2017-08-27_frontend_story/04_experience_vuejs.md rename to GDGKR/17-08-27_frontend_story/04_experience_vuejs.md diff --git a/GDGKR/2017-11-19_dev_fest/01_typescript_clean_architecture.md b/GDGKR/17-11-19_dev_fest/01_typescript_clean_architecture.md similarity index 100% rename from GDGKR/2017-11-19_dev_fest/01_typescript_clean_architecture.md rename to GDGKR/17-11-19_dev_fest/01_typescript_clean_architecture.md diff --git a/GDGKR/2017-11-19_dev_fest/02_mvc_mvvm.md b/GDGKR/17-11-19_dev_fest/02_mvc_mvvm.md similarity index 100% rename from GDGKR/2017-11-19_dev_fest/02_mvc_mvvm.md rename to GDGKR/17-11-19_dev_fest/02_mvc_mvvm.md diff --git a/GDGKR/2017-11-19_dev_fest/03_d3_basic.md b/GDGKR/17-11-19_dev_fest/03_d3_basic.md similarity index 100% rename from GDGKR/2017-11-19_dev_fest/03_d3_basic.md rename to GDGKR/17-11-19_dev_fest/03_d3_basic.md diff --git a/GDGKR/2017-11-19_dev_fest/04_android_things.md b/GDGKR/17-11-19_dev_fest/04_android_things.md similarity index 100% rename from GDGKR/2017-11-19_dev_fest/04_android_things.md rename to GDGKR/17-11-19_dev_fest/04_android_things.md diff --git a/GDGKR/2017-11-19_dev_fest/05_github_cloudflare.md b/GDGKR/17-11-19_dev_fest/05_github_cloudflare.md similarity index 100% rename from GDGKR/2017-11-19_dev_fest/05_github_cloudflare.md rename to GDGKR/17-11-19_dev_fest/05_github_cloudflare.md diff --git a/GDGKR/2017_March/00_google_cloud_next_extended.md b/GDGKR/17_March/00_google_cloud_next_extended.md similarity index 100% rename from GDGKR/2017_March/00_google_cloud_next_extended.md rename to GDGKR/17_March/00_google_cloud_next_extended.md diff --git a/GDGKR/2017_March/01_free_tier_gcp_serverless.md b/GDGKR/17_March/01_free_tier_gcp_serverless.md similarity index 100% rename from GDGKR/2017_March/01_free_tier_gcp_serverless.md rename to GDGKR/17_March/01_free_tier_gcp_serverless.md diff --git a/GDGKR/2017_March/02_gcp_key_updates.md b/GDGKR/17_March/02_gcp_key_updates.md similarity index 100% rename from GDGKR/2017_March/02_gcp_key_updates.md rename to GDGKR/17_March/02_gcp_key_updates.md diff --git a/GDGKR/2017_March/03_kubernetes_on_gcp.md b/GDGKR/17_March/03_kubernetes_on_gcp.md similarity index 100% rename from GDGKR/2017_March/03_kubernetes_on_gcp.md rename to GDGKR/17_March/03_kubernetes_on_gcp.md diff --git a/GDGKR/2018-08-20_chrome_dev_meetup_1/01_progressive_web_apps.md b/GDGKR/18-08-20_chrome_dev_meetup_1/01_progressive_web_apps.md similarity index 100% rename from GDGKR/2018-08-20_chrome_dev_meetup_1/01_progressive_web_apps.md rename to GDGKR/18-08-20_chrome_dev_meetup_1/01_progressive_web_apps.md diff --git a/GDGKR/2018-08-20_chrome_dev_meetup_1/02_scroll_snap.md b/GDGKR/18-08-20_chrome_dev_meetup_1/02_scroll_snap.md similarity index 100% rename from GDGKR/2018-08-20_chrome_dev_meetup_1/02_scroll_snap.md rename to GDGKR/18-08-20_chrome_dev_meetup_1/02_scroll_snap.md diff --git a/GDGKR/2018-08-20_chrome_dev_meetup_1/scroll_snap_exmaples/example_1.html b/GDGKR/18-08-20_chrome_dev_meetup_1/scroll_snap_exmaples/example_1.html similarity index 100% rename from GDGKR/2018-08-20_chrome_dev_meetup_1/scroll_snap_exmaples/example_1.html rename to GDGKR/18-08-20_chrome_dev_meetup_1/scroll_snap_exmaples/example_1.html diff --git a/GDGKR/2018-08-20_chrome_dev_meetup_1/scroll_snap_exmaples/example_2.html b/GDGKR/18-08-20_chrome_dev_meetup_1/scroll_snap_exmaples/example_2.html similarity index 100% rename from GDGKR/2018-08-20_chrome_dev_meetup_1/scroll_snap_exmaples/example_2.html rename to GDGKR/18-08-20_chrome_dev_meetup_1/scroll_snap_exmaples/example_2.html diff --git a/GDGKR/2018-11-10_dev_fest/00_intro.md b/GDGKR/18-11-10_dev_fest/00_intro.md similarity index 100% rename from GDGKR/2018-11-10_dev_fest/00_intro.md rename to GDGKR/18-11-10_dev_fest/00_intro.md diff --git a/GDGKR/2018-11-10_dev_fest/01_chrome_devtools.md b/GDGKR/18-11-10_dev_fest/01_chrome_devtools.md similarity index 100% rename from GDGKR/2018-11-10_dev_fest/01_chrome_devtools.md rename to GDGKR/18-11-10_dev_fest/01_chrome_devtools.md diff --git a/GDGKR/2018-11-10_dev_fest/02_about_spa_status_management.md b/GDGKR/18-11-10_dev_fest/02_about_spa_status_management.md similarity index 100% rename from GDGKR/2018-11-10_dev_fest/02_about_spa_status_management.md rename to GDGKR/18-11-10_dev_fest/02_about_spa_status_management.md diff --git a/GDGKR/2018-11-10_dev_fest/03_msa_istio.md b/GDGKR/18-11-10_dev_fest/03_msa_istio.md similarity index 100% rename from GDGKR/2018-11-10_dev_fest/03_msa_istio.md rename to GDGKR/18-11-10_dev_fest/03_msa_istio.md diff --git a/GDGKR/2018-11-10_dev_fest/04_ strict_javascript.md b/GDGKR/18-11-10_dev_fest/04_ strict_javascript.md similarity index 100% rename from GDGKR/2018-11-10_dev_fest/04_ strict_javascript.md rename to GDGKR/18-11-10_dev_fest/04_ strict_javascript.md diff --git a/GDGKR/2018-11-10_dev_fest/05_chromium_blink.md b/GDGKR/18-11-10_dev_fest/05_chromium_blink.md similarity index 100% rename from GDGKR/2018-11-10_dev_fest/05_chromium_blink.md rename to GDGKR/18-11-10_dev_fest/05_chromium_blink.md diff --git a/GDGKR/2018-11-28_devfest_webtech_2018/01_frontend_level_up_with_angular.md b/GDGKR/18-11-28_devfest_webtech_2018/01_frontend_level_up_with_angular.md similarity index 100% rename from GDGKR/2018-11-28_devfest_webtech_2018/01_frontend_level_up_with_angular.md rename to GDGKR/18-11-28_devfest_webtech_2018/01_frontend_level_up_with_angular.md diff --git a/GDGKR/2018-11-28_devfest_webtech_2018/02_history_of_min.md b/GDGKR/18-11-28_devfest_webtech_2018/02_history_of_min.md similarity index 100% rename from GDGKR/2018-11-28_devfest_webtech_2018/02_history_of_min.md rename to GDGKR/18-11-28_devfest_webtech_2018/02_history_of_min.md diff --git a/GDGKR/2018-11-28_devfest_webtech_2018/03_guide_for_frontend_traveler.md b/GDGKR/18-11-28_devfest_webtech_2018/03_guide_for_frontend_traveler.md similarity index 100% rename from GDGKR/2018-11-28_devfest_webtech_2018/03_guide_for_frontend_traveler.md rename to GDGKR/18-11-28_devfest_webtech_2018/03_guide_for_frontend_traveler.md diff --git a/GDGKR/2019-06-30_google_io_extended/01_what_is_new_in_web.md b/GDGKR/19-06-30_google_io_extended/01_what_is_new_in_web.md similarity index 100% rename from GDGKR/2019-06-30_google_io_extended/01_what_is_new_in_web.md rename to GDGKR/19-06-30_google_io_extended/01_what_is_new_in_web.md diff --git a/GDGKR/2019-06-30_google_io_extended/02_introduce_flutter.md b/GDGKR/19-06-30_google_io_extended/02_introduce_flutter.md similarity index 100% rename from GDGKR/2019-06-30_google_io_extended/02_introduce_flutter.md rename to GDGKR/19-06-30_google_io_extended/02_introduce_flutter.md diff --git a/GDGKR/2019-06-30_google_io_extended/03_google_seach_js_pages.md b/GDGKR/19-06-30_google_io_extended/03_google_seach_js_pages.md similarity index 100% rename from GDGKR/2019-06-30_google_io_extended/03_google_seach_js_pages.md rename to GDGKR/19-06-30_google_io_extended/03_google_seach_js_pages.md diff --git a/GDGKR/2019-07-13_io_extended_web_tech/01_puppeteer.md b/GDGKR/19-07-13_io_extended_web_tech/01_puppeteer.md similarity index 100% rename from GDGKR/2019-07-13_io_extended_web_tech/01_puppeteer.md rename to GDGKR/19-07-13_io_extended_web_tech/01_puppeteer.md diff --git a/GDGKR/2019-07-13_io_extended_web_tech/02_portals.md b/GDGKR/19-07-13_io_extended_web_tech/02_portals.md similarity index 100% rename from GDGKR/2019-07-13_io_extended_web_tech/02_portals.md rename to GDGKR/19-07-13_io_extended_web_tech/02_portals.md diff --git a/GDGKR/2019-07-13_io_extended_web_tech/03_new_capabilities_for_the_web.md b/GDGKR/19-07-13_io_extended_web_tech/03_new_capabilities_for_the_web.md similarity index 100% rename from GDGKR/2019-07-13_io_extended_web_tech/03_new_capabilities_for_the_web.md rename to GDGKR/19-07-13_io_extended_web_tech/03_new_capabilities_for_the_web.md diff --git a/GDGKR/2019-07-13_io_extended_web_tech/04_web_assembly_101.md b/GDGKR/19-07-13_io_extended_web_tech/04_web_assembly_101.md similarity index 100% rename from GDGKR/2019-07-13_io_extended_web_tech/04_web_assembly_101.md rename to GDGKR/19-07-13_io_extended_web_tech/04_web_assembly_101.md diff --git a/GDGKR/2019-07-13_io_extended_web_tech/05_going_big_pwa.md b/GDGKR/19-07-13_io_extended_web_tech/05_going_big_pwa.md similarity index 100% rename from GDGKR/2019-07-13_io_extended_web_tech/05_going_big_pwa.md rename to GDGKR/19-07-13_io_extended_web_tech/05_going_big_pwa.md diff --git a/GDGKR/2019-07-13_io_extended_web_tech/06_deep_in_lighthouse_audits.md b/GDGKR/19-07-13_io_extended_web_tech/06_deep_in_lighthouse_audits.md similarity index 100% rename from GDGKR/2019-07-13_io_extended_web_tech/06_deep_in_lighthouse_audits.md rename to GDGKR/19-07-13_io_extended_web_tech/06_deep_in_lighthouse_audits.md diff --git a/GDGKR/2019-07-13_io_extended_web_tech/sample/crawling.js b/GDGKR/19-07-13_io_extended_web_tech/sample/crawling.js similarity index 100% rename from GDGKR/2019-07-13_io_extended_web_tech/sample/crawling.js rename to GDGKR/19-07-13_io_extended_web_tech/sample/crawling.js diff --git a/GDGKR/2019-07-13_io_extended_web_tech/sample/package-lock.json b/GDGKR/19-07-13_io_extended_web_tech/sample/package-lock.json similarity index 100% rename from GDGKR/2019-07-13_io_extended_web_tech/sample/package-lock.json rename to GDGKR/19-07-13_io_extended_web_tech/sample/package-lock.json diff --git a/GDGKR/2019-07-13_io_extended_web_tech/sample/package.json b/GDGKR/19-07-13_io_extended_web_tech/sample/package.json similarity index 100% rename from GDGKR/2019-07-13_io_extended_web_tech/sample/package.json rename to GDGKR/19-07-13_io_extended_web_tech/sample/package.json diff --git a/GDGKR/2019-07-13_io_extended_web_tech/sample/pdf.js b/GDGKR/19-07-13_io_extended_web_tech/sample/pdf.js similarity index 100% rename from GDGKR/2019-07-13_io_extended_web_tech/sample/pdf.js rename to GDGKR/19-07-13_io_extended_web_tech/sample/pdf.js diff --git a/GDGKR/2019-07-13_io_extended_web_tech/sample/screenshot.js b/GDGKR/19-07-13_io_extended_web_tech/sample/screenshot.js similarity index 100% rename from GDGKR/2019-07-13_io_extended_web_tech/sample/screenshot.js rename to GDGKR/19-07-13_io_extended_web_tech/sample/screenshot.js diff --git a/GDGKR/2019-10-20_devfest_seoul/00_keynote.md b/GDGKR/19-10-20_devfest_seoul/00_keynote.md similarity index 100% rename from GDGKR/2019-10-20_devfest_seoul/00_keynote.md rename to GDGKR/19-10-20_devfest_seoul/00_keynote.md diff --git a/GDGKR/2019-10-20_devfest_seoul/01_functional_programming.md b/GDGKR/19-10-20_devfest_seoul/01_functional_programming.md similarity index 100% rename from GDGKR/2019-10-20_devfest_seoul/01_functional_programming.md rename to GDGKR/19-10-20_devfest_seoul/01_functional_programming.md diff --git a/GDGKR/2019-10-20_devfest_seoul/02_web_gpu_is_coming.md b/GDGKR/19-10-20_devfest_seoul/02_web_gpu_is_coming.md similarity index 100% rename from GDGKR/2019-10-20_devfest_seoul/02_web_gpu_is_coming.md rename to GDGKR/19-10-20_devfest_seoul/02_web_gpu_is_coming.md diff --git a/GDGKR/2019-10-20_devfest_seoul/03_atomic_design_pattern.md b/GDGKR/19-10-20_devfest_seoul/03_atomic_design_pattern.md similarity index 100% rename from GDGKR/2019-10-20_devfest_seoul/03_atomic_design_pattern.md rename to GDGKR/19-10-20_devfest_seoul/03_atomic_design_pattern.md diff --git a/GDGKR/2019-10-20_devfest_seoul/04_web_service_msa.md b/GDGKR/19-10-20_devfest_seoul/04_web_service_msa.md similarity index 100% rename from GDGKR/2019-10-20_devfest_seoul/04_web_service_msa.md rename to GDGKR/19-10-20_devfest_seoul/04_web_service_msa.md diff --git a/GDGKR/2019-10-20_devfest_seoul/05_vertical_align.md b/GDGKR/19-10-20_devfest_seoul/05_vertical_align.md similarity index 100% rename from GDGKR/2019-10-20_devfest_seoul/05_vertical_align.md rename to GDGKR/19-10-20_devfest_seoul/05_vertical_align.md diff --git a/GDGKR/2019-11-23_devfest_webtech_2019/00_keynote.md b/GDGKR/19-11-23_devfest_webtech_2019/00_keynote.md similarity index 100% rename from GDGKR/2019-11-23_devfest_webtech_2019/00_keynote.md rename to GDGKR/19-11-23_devfest_webtech_2019/00_keynote.md diff --git a/GDGKR/2019-11-23_devfest_webtech_2019/01_visbug_opensource.md b/GDGKR/19-11-23_devfest_webtech_2019/01_visbug_opensource.md similarity index 100% rename from GDGKR/2019-11-23_devfest_webtech_2019/01_visbug_opensource.md rename to GDGKR/19-11-23_devfest_webtech_2019/01_visbug_opensource.md diff --git a/GDGKR/2019-11-23_devfest_webtech_2019/02_main_thread_with_code.md b/GDGKR/19-11-23_devfest_webtech_2019/02_main_thread_with_code.md similarity index 100% rename from GDGKR/2019-11-23_devfest_webtech_2019/02_main_thread_with_code.md rename to GDGKR/19-11-23_devfest_webtech_2019/02_main_thread_with_code.md diff --git a/GDGKR/2019-11-23_devfest_webtech_2019/03_http3_performance.md b/GDGKR/19-11-23_devfest_webtech_2019/03_http3_performance.md similarity index 100% rename from GDGKR/2019-11-23_devfest_webtech_2019/03_http3_performance.md rename to GDGKR/19-11-23_devfest_webtech_2019/03_http3_performance.md diff --git a/GDGKR/2019-11-23_devfest_webtech_2019/04_modern_web_assembly.md b/GDGKR/19-11-23_devfest_webtech_2019/04_modern_web_assembly.md similarity index 100% rename from GDGKR/2019-11-23_devfest_webtech_2019/04_modern_web_assembly.md rename to GDGKR/19-11-23_devfest_webtech_2019/04_modern_web_assembly.md diff --git a/GDGKR/2019-11-23_devfest_webtech_2019/05_video_editor_on_web.md b/GDGKR/19-11-23_devfest_webtech_2019/05_video_editor_on_web.md similarity index 100% rename from GDGKR/2019-11-23_devfest_webtech_2019/05_video_editor_on_web.md rename to GDGKR/19-11-23_devfest_webtech_2019/05_video_editor_on_web.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/00_keynote.md b/GDGKR/20-12-11_chrome_dev_summit_2020/00_keynote.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/00_keynote.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/00_keynote.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/01_what's_new_in_dev_tools.md b/GDGKR/20-12-11_chrome_dev_summit_2020/01_what's_new_in_dev_tools.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/01_what's_new_in_dev_tools.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/01_what's_new_in_dev_tools.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/02_extending_css_with_houdini.md b/GDGKR/20-12-11_chrome_dev_summit_2020/02_extending_css_with_houdini.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/02_extending_css_with_houdini.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/02_extending_css_with_houdini.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/03_transitioning_to_modern_javascript.md b/GDGKR/20-12-11_chrome_dev_summit_2020/03_transitioning_to_modern_javascript.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/03_transitioning_to_modern_javascript.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/03_transitioning_to_modern_javascript.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/04_structured_data_for_developers.md b/GDGKR/20-12-11_chrome_dev_summit_2020/04_structured_data_for_developers.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/04_structured_data_for_developers.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/04_structured_data_for_developers.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/05_making_the_web_more_visual_with_web_stories.md b/GDGKR/20-12-11_chrome_dev_summit_2020/05_making_the_web_more_visual_with_web_stories.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/05_making_the_web_more_visual_with_web_stories.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/05_making_the_web_more_visual_with_web_stories.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/06_the_web_ahead.md b/GDGKR/20-12-11_chrome_dev_summit_2020/06_the_web_ahead.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/06_the_web_ahead.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/06_the_web_ahead.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/07_state_of_speed_tooling.md b/GDGKR/20-12-11_chrome_dev_summit_2020/07_state_of_speed_tooling.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/07_state_of_speed_tooling.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/07_state_of_speed_tooling.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/08_fixing_common_web_vitals_issues.md b/GDGKR/20-12-11_chrome_dev_summit_2020/08_fixing_common_web_vitals_issues.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/08_fixing_common_web_vitals_issues.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/08_fixing_common_web_vitals_issues.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/09_improve_ux_patterns_optimized_for_core_web_vitals.md b/GDGKR/20-12-11_chrome_dev_summit_2020/09_improve_ux_patterns_optimized_for_core_web_vitals.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/09_improve_ux_patterns_optimized_for_core_web_vitals.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/09_improve_ux_patterns_optimized_for_core_web_vitals.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/10_exploring_the_future_of_core_web_vitals.md b/GDGKR/20-12-11_chrome_dev_summit_2020/10_exploring_the_future_of_core_web_vitals.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/10_exploring_the_future_of_core_web_vitals.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/10_exploring_the_future_of_core_web_vitals.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/11_beyond_fast.md b/GDGKR/20-12-11_chrome_dev_summit_2020/11_beyond_fast.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/11_beyond_fast.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/11_beyond_fast.md diff --git a/GDGKR/2020-12-11_chrome_dev_summit_2020/12_3x3_seo_tips.md b/GDGKR/20-12-11_chrome_dev_summit_2020/12_3x3_seo_tips.md similarity index 100% rename from GDGKR/2020-12-11_chrome_dev_summit_2020/12_3x3_seo_tips.md rename to GDGKR/20-12-11_chrome_dev_summit_2020/12_3x3_seo_tips.md diff --git a/GDGKR/22-12-09_dev_fest/01_linux_kernel.md b/GDGKR/22-12-09_dev_fest/01_linux_kernel.md new file mode 100644 index 0000000..4fff3bb --- /dev/null +++ b/GDGKR/22-12-09_dev_fest/01_linux_kernel.md @@ -0,0 +1,114 @@ +# 수 년간 전공책만 팠던 개발자의 리눅스 커널 기여 삽질기 +- 강민철 + +## 주제 +- 전공 지식과 프로덕션 코드와의 간극을 메꿔야한다 +- 오픈소스 기여를 통해 성장할 수 있다 + +## 목차 +- 리눅스 커널에 기여하기까지 +- 전공 지식과 프로덕션 코드와의 간극 +- 리눅스 커널 기여 삽질기와 얻게된 것들 +- 정리 + +## 리눅스 커널에 기여하기까지 + +### 개발을 잘하고 싶다 +- 막연했던 개발자 지망생 시절 + - 전공서 읽어보면 되나? + - C, 컴퓨터구조 등... +- 이론적 내용의 실제 구현이 궁금해지기 시작 +- 개발자는 코드로 말하는 사람 + - 커널 코드를 읽어보자! + +### 개인 프로젝트를 만들어봐야겠다 +- 사비 들여 수 개월간 개발한 야심찬 개인 프로젝트의 실패 + - 사용자 없는 프로그램은 세상에 존재하지 않는 프로그램 +- 실제로 사용되는 거대한 프로그램은 어떻게 개발/유지보수될까? + - 커널 코드를 읽어보자 + +## 전공 지식과 프로덕션 코드와의 간극 + +### 리눅스 커널에 기여하기까지 +- But, 전공서를 읽었어도 난해했던 리눅스 커널... 왜일까? +- 전공 지식만으로는 습득할 수 없는 지식들 + +### 교집합 +- 분석 대상의 용어 & 기본 동작 +- 모르면 시작이 안되는 것들 + - What is kernel + - Kernel Architeture + - Packet Encapsualtion/uncapsulation +- 에러 메시지 독해 + - 정도껏 구글링하는 발판 +- 성능, 용량, 비용 + - CS 지식의 목적이자 개발의 목적 +- 커널 코딩 테크닉 + - Kernel 주요 자료구조 + - etc..... + +### 필요했던 것 +- 코드 커뮤니케이션 +- 테스팅 + - KUnit +- 코드 분석/디버깅 + - kdb + - perf +- 최신/지엽적 기술들 + - kernal config / build + - eBPF + - Rust + +## 리눅스 커널 기여 삽질기와 얻게된 것들 +- 커널 기여 전 마음가짐: 신중해야 한다 +- 커널 기여 전 읽어야 할 자료 + - https://kernelnewbies.org +- 기여 프로세스 + - 커널 소스 파일 clone + - 커널 빌드 + - 작업 + - 패치 파일 생성 + - 테스트 + - 이메일 발송 +- 몰라 일단, sudo make allyesconfig => 너무 느린 빌드 속도 + - kernel config 공부는 피할수 없다 + +### 첫 기여 어디에 했을까? +- 관심있는 분야요 => 기여하기엔 너무 완벽한데? => I don't want your code! +- 개발 프로세스를 익히자: /drivers/staging => 뉴비들의 시작 점 +- 무엇을 기여할까?: static analysis tool / 의존은 금물, 떄론 거짓말을 한다 + - sparse + - smatch + - coccinelle +- 커밋 메시지는 코드만큼 중요하다 + - `signed-off-by` +- 아무리 사소해도 Testing은 필수 +- 명령어로 이메일로 보내기 (git, mutt) + - Github에서 관리하는 코드가 아니기에, 이메일로 주고 받음 +- 첫 기여 이후, 이제는 관심있는 분야로... + +### 얻게 된 것들 +- 전공서에서나 볼 법한 스타 개발자들의 피드백 + - 피드백은 곧 멘토링 +- 커밋 로그: 개인 프로젝트로는 얻을 수 없었떤 대규모 프로그램 개발 경험 +- commit log/mailing list를 통한 Best-practice 학습 + - 고연차 고수 개발자분들은 어떻게 코드를 작성하는지/문제를 해결하는지 + - 고수 개발자는 어떤 논의를 하는지 +- 자부심 & 성취감 +- 영어 구술 능력 + +## 정리 +- 전공 지식과 프로덕션 코드의 간극 메꾸기 +- 오픈소스 기여를 통한 성장 + +### 전공 지식과 프로덕션 코드의 간극 메꾸기 +- CS 지식은 반드시 필요하다: 비전공자 개발자가 반드시 배양해야 할 덕목 + - +- 모든 개발자 지망생이 배양해야할 덕목 + - 코드 커뮤니케이션 + - 테스팅 + - 코드 분석/디버깅 + - 최신 지엽적인 기술들 + +### 오픈소스 기여를 통한 성장 +- 꼭 Kernel이 아니어도 된다 diff --git a/GDGKR/22-12-09_dev_fest/02_service_end.md b/GDGKR/22-12-09_dev_fest/02_service_end.md new file mode 100644 index 0000000..7f7573f --- /dev/null +++ b/GDGKR/22-12-09_dev_fest/02_service_end.md @@ -0,0 +1,84 @@ +# 네? 갑자기 서비스를 종료한다구요? +- 조민국 / 네이버 예약/주문 개발팀 + +## 개발자가 이직을 하게 만드는 요소들 +- 연봉이 낮아서 +- 개발 문화가 안 좋아서 +- **운영 중이던 서비스가 종료될 떄** +- 기타 등등... + +## 서비스가 문을 닫으면 개발자가 해야 하는 일들 +- 기존 데이터를 다른 팀들에게 넘겨주어야함 +- 더 이상 새로운 유저는 받지 않음 + +### 팀 해체 +- 공식적인 서비스 종료 공지와 함꼐 팀 해체 선언 + +### 서비스가 종료가 결정된 이후 +- 개인이 원하는 다른 팀으로의 이동할 수 있는 기회가 생김 +- 팀을 이동하게 되면 새로운 팀원들과 새로운 환경에 다시 적응해야 함 +- 그렇다면 이직을 하는 것과 크게 다르지 않은 것 같은데? + +### 서비스 종료를 들은 개발자의 심정 +- 밤낮으로 애정하며 만든 서비스가 종료되는 것은 가슴 아픈 일 +- 한동안 우울감... + +> 나가자, 근데 어디로? + +## 이직할 회사를 고르는 기준 +- 동료 +- 도메인 +- 재미 +- 워라밸 +- 연봉 + +## 흔한 개발자 면접 프로세스 +- 이력서 +- 코딩 테스트 or 과제 +- 전화 면접 (없을 수도 있음) +- **1차 기술 면접** +- 2차 면접 (인성 or 기술) +- 3차 면접 (없을 수도 있음) + +## 1차 기술 면접 +- 일종의 방지턱 +- 질문 => 답변 => 질문 => 답변 => 질문 => 답변, 무한 루프 +- 알고리즘 / 시스템 디자인 실리콘밸리 스타일식 면접 +- Why? - 왜요? 꼬리에 꼬리를 무는 면접 + +### 질문/답변 무한 루프 면접 준비법 +- 개발자 면접 짊문을 달달 외운다 +- 공식 레퍼런스 기능까지 외운다 +- 특정 기술 분야를 DFS로 탐색하면서 공부한다 +- KDS(Keyword Driven Study) + +### 알고리즘/시스템 디자인 면접 준비법 +- 릿코드 열심히 푼다 +- 내가 어떻게 그 문제를 풀었는지를 설명하는 것을 연습한다 +- System Design Interview에 관련된 책을 읽는다 + - 가상 면접 사례로 배우는 대규모 시스템 설계 기초 + +### 왜? 꼬리에 꼬리르 무는 기술 면접 준비법 +- 내가 사용하는 기술이 어떤 문제를 풀기 위해 세상에 나왔는지 생각해본다. + - 예시: 이거 왜 썼어요? + - 안 좋은 답: 팀장님이 시켜서요, 그냥 팀에서 이거 썼어요 + - 좋은 답: XX 문제가 있었고, 이 문제를 풀 수 있는 적합한 도구로 A,B가 있었다. 팀 상황을 고려했을 떄 A가 적합하다 판단하여 선택했다 + +### 왜 굳이 이런 면접을? +- 회사는 좋은 개발자를 원한다 + +## 좋은 개발자란 뭘까? +- 기술에 대한 이해가 깊고 문제 해결 능력이 뛰어난 테크니션?? +- 커뮤니케이션 능력과 화술이 뛰어나서 스펙을 쥐락펴락하는 달변가?? +- 좋은 개발자는 육각형 개발자 + - 개발지식 + - 커뮤니케이션 + - 문제 해결 능력 + - 공유 + - 성장 욕구 + - 학습 능력 + +## 이직 과정을 통해 배운 것들 +- 면접 과정을 통해 지식을 점검하고 새로운 것들을 배우며 성장할 수 있는 좋은 기회였다 +- 세상은 넓고 고수는 많다 +- 기술 면접에 합격하기 위해서는 꾸준히 공부하는 것이 중요하다는 것을 다시 한 번 느꼈다 diff --git a/GDGKR/22-12-09_dev_fest/03_8years_startup.md b/GDGKR/22-12-09_dev_fest/03_8years_startup.md new file mode 100644 index 0000000..f29742d --- /dev/null +++ b/GDGKR/22-12-09_dev_fest/03_8years_startup.md @@ -0,0 +1,68 @@ +# 한 스타트업만 8년간 다닌 개발자의 회고, 그리고 겨울 +- 류기민 / HOLIX + +## 나의 유일한 경험 S전자 +- 무선사업부 하계인턴(학교 프로그램) +- 부품처럼 사는 삶 간접 경험 +- 숨막히는 사회생활 + - 이 바닥 좁아요 + - 부장님한테 폴더 인사하는 인턴 +- 7~8살 많은 신입이 받고 있는 교유 = 직전 학기에 들은 과목 +- `나에게는 시간이 있다` + +## 개발은 처음이라.. +- 소프트웨어 보다는 하드웨어에 친숙했던... + +## 3개월만에 앱 출시 +- 웹으로만 운영하던 서비스에서 안드로이드 앱으로 서비스를 조금 더 튼튼하게 만든 느낌 + +## 작지만 강한 개발팀 +- 물고기를 잡는 법을 알려주는 동료 + +## 스스로 성장했다고 느낀 순간 +- 안드로이드 앱 리드 +- 관리자 페이지 구축 +- 백엔드 +- 프론트엔드 리뉴얼 + +## 한 회사의 생존과 성장을 함꼐 했다 +- 회사와 함꼐하는 J커브 + +## 내가 겪은 고민들 +- 대체 인력이 없다 => 개인에게 의존성이 없는 환경을 만들어서 해결 + - 몽골에서 아바타 코딩 시켜서 배포 +- 회사의 성장에 맞춰 내가 맞춰서 성장할 수 있을까? + - 안드로이드 앱 개발은 핵심적인 부분이 아니지 않을까? + - 솔직한 커뮤니케이션 +- 고립되는 느낌 + - 내가 짠 코드가 최선의 구현 방식일까? + - 나의 의사결정이 최선의 결정이었을까? + - 빅테크에 대한 호기심 + - 나의 객관적 능력의 레벨은? + - 고민을 나눌 상대가 없다 + +## 오랫동안 한 회사에 있을 수 있었던 이유 +- 회사가 오래 살아 남아야 한다 +- 들어오면서 기대했던 것과 일치 +- 오랜 기간 쌓은 창업자와 신뢰 관계 +- 다양한 노력들 + - 회사가 필요로 하는 역량 수준에 맞추기 + - 공적/사적 감정 구분하기 + - 장/단점을 파악하고 인정하기 + - 자신만의 멘탈 관리법 찾기 + +## 겨울이 왔습니다 +- 경제 위기로 인한 악화 + +> 끝까지 해보자 + +## 2022 VS 2015 +- 개개인의 역량 +- 고객 +- 매출 +- 제품 +- 자급자족 가능성 + +> 겨울? 오히려 좋아 + +> 어려운 시점은 인생이라는 드라마의 하나의 에피소드 diff --git a/GDGKR/22-12-09_dev_fest/04_winter_resign.md b/GDGKR/22-12-09_dev_fest/04_winter_resign.md new file mode 100644 index 0000000..b4298e5 --- /dev/null +++ b/GDGKR/22-12-09_dev_fest/04_winter_resign.md @@ -0,0 +1,72 @@ +# 겨울이 왔습니다. 저는 퇴사합니다. +- 이승민 / GDE Android Korea + +## 목차 +- 퇴사 배경 +- 퇴사 이후 계획 +- 두려움과 설레임 +- 마무리 + +## 퇴사 배경 + +### 어떻게 사는게 행복할까... +- 수직적 경험: 깊게 파고 들어서 그루가 될까? +- 수평적 경험: 다양하게 알고 제너럴 리스트가 될까? + +### 뱅크샐러드에 입사하면서... +- 다음 커리어는 다르게 가고 싶다 + - 해외 취업 + - 창업 + +### 뱅크샐러드에서의 3년 10개월 +- Android 개발자 1년 +- Engineering Manager 1년 6개월 +- Product Manager 1년 4개월 +- 여기서 할 수 있는 대부분의 것을 경험하였다 + +## 퇴사 이후의 계획 +- 해외 취업 +- **창업** => 풀어보고 싶은 문제가 생겼어요 + +### 창업 계획 +- 혼자 서비스 런칭까지 목표 +- MVP 개발 중 + - flutter + - node.js + +### 또 하나의 계획 - 유튜브 +- 스페인 산티아고 순례길 +- 앞으로의 여행 +- 개발 강의 +- 커리어, 스타트업 썰 +- 개발자 인터뷰 + +## 두려움과 설레임 +- 겨울이 왔습니다 + +### 따뜻한 지붕 +- 연봉 +- 스톡옵션 +- 고용안정 + +### 두려움 +- 혼자 서비스 런칭할 수 있을까? +- 런칭 후 마케팅/운영은 어떻게 하지? +- 유튜브 조회수는 언제 오를까? +- 당장 나갈 큰 돈은 어디서 구하지? +- **이렇게 했는데 안되면 어떡하지?** + +> 역량으로 돈 버는 것은 해봤다. 내 컨텐츠로 올라가보자 + +### 회사 다니면서 하면 되지 않아? +- 나는 게으르다 +- 집을 부숴야 움직인다 + +### 장이 안좋은데 겨울 지나고 하는게 좋지 않아? +- 시장이 조용하니 조바심이 없다 +- 내공을 쌓으며 봄을 대비하자 + +> 중요한 것은 꺾이지 않는 마음 + +## 마무리 +- 행복을 위해 도전합니다 From b94ca43c61fdb73f2db5ed2656c2436531acafbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Dec 2022 23:09:04 +0900 Subject: [PATCH 3/8] Bump minimatch in /GDGKR/19-07-13_io_extended_web_tech/sample (#21) Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2. - [Release notes](https://github.com/isaacs/minimatch/releases) - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2) --- updated-dependencies: - dependency-name: minimatch dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../19-07-13_io_extended_web_tech/sample/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GDGKR/19-07-13_io_extended_web_tech/sample/package-lock.json b/GDGKR/19-07-13_io_extended_web_tech/sample/package-lock.json index 0d34c9e..8b27fc2 100644 --- a/GDGKR/19-07-13_io_extended_web_tech/sample/package-lock.json +++ b/GDGKR/19-07-13_io_extended_web_tech/sample/package-lock.json @@ -174,9 +174,9 @@ "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } From c8b2c07730818b22618c22be952c70de972aacde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Dec 2022 23:09:14 +0900 Subject: [PATCH 4/8] Bump minimist and extract-zip (#22) Bumps [minimist](https://github.com/minimistjs/minimist) and [extract-zip](https://github.com/maxogden/extract-zip). These dependencies needed to be updated together. Updates `minimist` from 0.0.8 to 1.2.7 - [Release notes](https://github.com/minimistjs/minimist/releases) - [Changelog](https://github.com/minimistjs/minimist/blob/main/CHANGELOG.md) - [Commits](https://github.com/minimistjs/minimist/compare/v0.0.8...v1.2.7) Updates `extract-zip` from 1.6.7 to 1.7.0 - [Release notes](https://github.com/maxogden/extract-zip/releases) - [Commits](https://github.com/maxogden/extract-zip/compare/v1.6.7...v1.7.0) --- updated-dependencies: - dependency-name: minimist dependency-type: indirect - dependency-name: extract-zip dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../sample/package-lock.json | 80 ++++++++++--------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/GDGKR/19-07-13_io_extended_web_tech/sample/package-lock.json b/GDGKR/19-07-13_io_extended_web_tech/sample/package-lock.json index 8b27fc2..aabaa77 100644 --- a/GDGKR/19-07-13_io_extended_web_tech/sample/package-lock.json +++ b/GDGKR/19-07-13_io_extended_web_tech/sample/package-lock.json @@ -31,6 +31,11 @@ "concat-map": "0.0.1" } }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -79,14 +84,14 @@ } }, "extract-zip": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", - "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", "requires": { - "concat-stream": "1.6.2", - "debug": "2.6.9", - "mkdirp": "0.5.1", - "yauzl": "2.4.1" + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" }, "dependencies": { "debug": { @@ -97,21 +102,43 @@ "ms": "2.0.0" } }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "requires": { + "pend": "~1.2.0" + } + }, + "minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } } } }, - "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "requires": { - "pend": "~1.2.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -181,19 +208,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -304,14 +318,6 @@ "requires": { "async-limiter": "~1.0.0" } - }, - "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", - "requires": { - "fd-slicer": "~1.0.1" - } } } } From 8ed90893d76a69acea67c5b5cfa8de6ca7be23eb Mon Sep 17 00:00:00 2001 From: Conan Kunhwan Ahn Date: Fri, 23 Dec 2022 17:42:02 +0900 Subject: [PATCH 5/8] if(kakao) 2022 Frontend (#19) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * mfa 문서 추가 * 웹 반응성 문서 추가 * 브런치 FE 심폐소생술 * react-query 전환기 문서 추가 * 광고 웹 SDK 개편기 문서 추가 * 사진 편집기, WebGL 문서 추가 * 코드 가독성 개선 문서 추가 * Svelte 상태 관리 라이브러리 개발기 문서 추가 * 마이크로 프론트엔드 문서 추가 * CSR 환경 고도화 문서 추가 * 에러처리 문서 추가 --- Kakao/2022/frontend/01_mfa.md | 184 ++++++++ ...1_\353\260\230\354\235\221\354\204\261.md" | 171 +++++++ ...20\354\206\214\354\203\235\354\210\240.md" | 125 +++++ Kakao/2022/frontend/04_react-query.md | 426 ++++++++++++++++++ ...2\264\221\352\263\240_\354\233\271_SDK.md" | 266 +++++++++++ ...04\355\216\270\354\247\221\352\270\260.md" | 196 ++++++++ ...34\352\260\200\353\217\205\354\204\261.md" | 364 +++++++++++++++ ...01\355\203\234\352\264\200\353\246\254.md" | 356 +++++++++++++++ Kakao/2022/frontend/09_micro_frontend.md | 59 +++ ...5_\352\263\240\353\217\204\355\231\224.md" | 138 ++++++ ...20\353\237\254\354\262\230\353\246\254.md" | 406 +++++++++++++++++ 11 files changed, 2691 insertions(+) create mode 100644 Kakao/2022/frontend/01_mfa.md create mode 100644 "Kakao/2022/frontend/02_\354\233\271_\353\260\230\354\235\221\354\204\261.md" create mode 100644 "Kakao/2022/frontend/03_\353\270\214\353\237\260\354\271\230_FE_\354\213\254\355\217\220\354\206\214\354\203\235\354\210\240.md" create mode 100644 Kakao/2022/frontend/04_react-query.md create mode 100644 "Kakao/2022/frontend/05_\352\264\221\352\263\240_\354\233\271_SDK.md" create mode 100644 "Kakao/2022/frontend/06_\354\202\254\354\247\204\355\216\270\354\247\221\352\270\260.md" create mode 100644 "Kakao/2022/frontend/07_\354\275\224\353\223\234\352\260\200\353\217\205\354\204\261.md" create mode 100644 "Kakao/2022/frontend/08_\354\203\201\355\203\234\352\264\200\353\246\254.md" create mode 100644 Kakao/2022/frontend/09_micro_frontend.md create mode 100644 "Kakao/2022/frontend/10_csr_\355\231\230\352\262\275_\352\263\240\353\217\204\355\231\224.md" create mode 100644 "Kakao/2022/frontend/11_\354\227\220\353\237\254\354\262\230\353\246\254.md" diff --git a/Kakao/2022/frontend/01_mfa.md b/Kakao/2022/frontend/01_mfa.md new file mode 100644 index 0000000..9cf3f79 --- /dev/null +++ b/Kakao/2022/frontend/01_mfa.md @@ -0,0 +1,184 @@ +# MFA 누구냐 너: 공통 플랫폼 파트의 MFA 도입기 +- [Youtube](https://www.youtube.com/watch?v=_SkngG2RR3Q&t=28s) +- [발표자료](https://speakerdeck.com/kakao/mfa-nugunya-neo-gongtong-peulraespom-pateuyi-mfa-doibgi) + +## 목차 +- 우리가 겪었던 불편 +- MFA가 무엇일까? +- MFA 구현 방법 +- 도입 효과 및 해결과제 + +## 우리가 겪었던 불편 +- 규모가 커진 조직에서 겪게되는 프론트엔드 불편 +- 중복 개발의 늪 + - 서로 다른 애플리케이션에서 비슷한 UI를 계속 만듦 + - UI만 살짝 다를 뿐 표현하고자 하는 데이터는 동일한 케이스가 발생 +- 업무 결합도 증가 + - 하나의 애플리케이션에 대해서 서로 다른 조직이 같이 운영하는 케이스 발생 + - 이로 인해 기획, 개발, 테스트 등의 일정에 대한 조율이 필요함 +- 응집도가 높고, 작은 단위의 애플리케이션 개발로 결합도를 낮추자! => MFA(Micro Frontend Architecture) + +## MFA가 무엇일까? +- 독립적으로 작동하는 SPA => Micro App +- 조각이 모여 하나의 App => Container App + +### MFA 장점 +- **응집력** 있는 코드 베이스를 가질 수 있다 + - 분리된 프론트엔드 애플리케이션의 속성에 응집력있는 코드로 유지가 가능하다 +- 유지보수, 배포 등 업무의 **분리** + - 코드 관리, 배포 등 조직 단위로 이루어지는 업무들이 Micro APp 단위로 분리되므로 앱 마다 자율적이고 확장적 개발이 가능하다 +- **점진적** 업데이트 + - 각 조직들은 각자 맡은 애플리케이션에 대해 자율적으로 업데이트할 수 있으므로, Container App의 관점에서 점진적 업데이트가 가능하다 + +## MFA 구현 방법 + +### MFA를 구현하는 방법 5가지 +- Iframe을 통핱 통합 + - Container App에서 Micro App을 iframe방식으로 표출하는 방식 + - 가장 간단하고 확실하지만, Micro <-> Container 앱간 통신에 제약이 있으며, Micro App이 깔끔하게 붙기 힘들다 +- Web Component를 통한 Runtime 통합 + - Micro App을 하나의 Custom element로 만들어 Container App에서 Runtime환경에서 Injection하는 방식 + - Micro App Injection 시, 오브젝트 형식의 많은 데이터를 Attribute를 통한 string 타입으로 데이터를 전달해야 하는 불편함이 있음 +- NGINX를 통한 Routing + - NGINX에서 유입된 URL에 따라 표출할 HTML을 결정 + - 단순한 Routing 정도 수준이지만 각 어플리케이션들이 분리되어 개발, 배포되어 있어야 하기 때문에 MFA의 방식 중 하나라고 할 수 있다 + - Micro App을 완전한 조각으로 이용하고 싶었기에 맞지 않았음 +- NPM화를 통한 Build time 통합 + - 각각의 Micro App들을 npm화 하여 Container App에서 install하여 이용하는 방식 + - Build time에 조합이 되기 때문에 각 조직간의 배포를 완전히 분리할 순 없다 +- Runtime JS, CSS Injection + - Build된 Micro App의 JS를 Container App에서 Runtime에 다운로드하여 Injection하는 방식 + - 과도한 페이로드 크기가 단점이 될 수 있다. + +### Runtime JS, CSS Injection 구현 방식 +- Container App에서 Micro App의 manifest.json 요청 +```json +{ + "app.css": "/style.css", + "app.js": "/app.js", + "app.js.LICENSE.tst": "/app.js.LICENSE.text", + "index.html": "/index.html" +} +``` +- manifest.json 내부의 js와 css파일 요청 +- npm 모듈을 사용하여 둘간의 연결 만들기 +```TypeScript +// Micro App 기준 +export function makeMicroApp({ appName, renderFunction }: IMakeMicroApp) { + if (!window[appName]) { + const microApp: MicroAppProps = { + /** + Micro App이 Render될 때 실행하는 함수 + index.tsx에서 실행할 로직이 여기서 실행된다 + */ + render: (props: renderProps) => { + // ... + renderFunction(props); + }, + /** + Micro App이 unmount될 때 실행되며, JS, CSS파일이 아래 cacheTime 이후에 delete된다 + */ + unmount: (containerId: string) => { + // ... + unmountComponentAtNode(unmountElement); + }, + /** + Micro App이 unmount되더라도 JS, CSS 파일이 바로 삭제되지 않고 유지되는 시간 + */ + cacheTime: 1000 * 300, + /** + Micro App unmount시에 실행되어야 할 logic이 stack으로 저장된다 + */ + unmountCallbackStack: [], + timeout: undefined, + }; + + if (window[appName] === undefined) { + window[appName] = microApp; + } + } +} +``` +```TypeScript +// Micro App 기준 +makeMicroApp({ + appName: 'microApp', + renderFunction: () => { + // ... + // {index.tsx logic} + // ... + }, +}); +``` +```TypeScript +import React from 'react'; + +export class MicroAppPage extends React.Component { + async fetchManifest() { + const { onloadScript } = this.props; + // ... + const appData: IManifestType = await getMicroAppInfo(appInfo.url + '/manifest.json'); + + injectionScript(/*...*/); + injectionStyle(/*...*/); + + return appData; + } + + init() { + const { onloadScript } = this.props; + // ... + const microApp = window[appInfo.appName]; + + if (microApp) { + onloadScript(); + } else { + this.fetchManifest(); + } + } + + componentDidMount() { + this.init(); + } + + componentWillUnmount() { + const microApp = window[appInfo.appName]; + // ... + try { + if (microApp) { + microApp.unmountCallbackStack.push(() => { + //... + scriptEle?.remove(); + styleEle?.remove(); + }); + microApp.unmount(appInfo.containerId); + } + } catch (error) { + console.error(error); + } + } + + render() { + // ... + return
+ } +} +``` + +## 도입 효과 및 해결과제 +### 도입 효과 +- 업무 결합도 감소 + - 하나의 애플리케이션으로 묶여있어 상호 결합도를 갖고 있던 업무를 분리하여 업무 결합도를 낮출 수 있었다 +- 컴포넌트 재사용성 증가 + - SPA를 Micro App으로 이용할 수 있게되어 재사용성이 증가하였고, 큰 조직 단위에서의 효율이 증대되었다 + +### 해결과제 +- 과도한 payload크기 + - 공통적으로 사용하는 library가 bundling된 js마다 존재하므로 payload크기가 과도하게 된다. + - 코드 스플리팅, 트리 쉐이킹 등을 지원하는 모던 프레임워크에서는 과도한 payload크기는 문제가 될 수 있다 +- 운영 복잡도 증가 + - Micro App마다 다른 빌드, 배포 파이프라인을 갖고 있어야 하기 때문에 관리 포인트가 늘어나게 된다 +- CSS 오염 문제 + - ShadowDom이나 CSS in JS 같은 방식이 아닌 Micro App의 CSS가 Container App과 다른 MicroApp의 CSS를 오염시킬 수 있다 +- Global window 공유 + - Container App과 모든 Micro App이 하나의 window를 공유하므로 Window 하부에 하나로 이용되는 Object가 어떠한 Micro App에서 변경되면 오류사항이 모든 MicroApp으로 퍼질 수 있다 diff --git "a/Kakao/2022/frontend/02_\354\233\271_\353\260\230\354\235\221\354\204\261.md" "b/Kakao/2022/frontend/02_\354\233\271_\353\260\230\354\235\221\354\204\261.md" new file mode 100644 index 0000000..309c83f --- /dev/null +++ "b/Kakao/2022/frontend/02_\354\233\271_\353\260\230\354\235\221\354\204\261.md" @@ -0,0 +1,171 @@ +# 웹 반응성 개선하기 +- [Youtube](https://www.youtube.com/watch?v=lmjfSgkpJr8) +- [발표자료](https://speakerdeck.com/kakao/web-baneungseong-gaeseonhagi) + +## 목차 +- 웹 성능이란? +- 웹 반응성 +- 웹 반응성 지표 +- 웹 반응성 측정 방법 +- 웹 반응성 개선 사례 + +## 웹 성능이란? +- 로딩 성능을 말하는 경우가 많음 +- 사용자는 빠르게 로드되지 않는 페이지는 떠나기 마련, 그렇기에 로딩 성능을 개선하는 것이 중요하다 + +### Kakao의 성능 측정 +- Synthetic Monitoring: 일정한 환경 속에서 성능 측정 +- Rear User Monitoring: 유저 환경 속에서 성능 측정 + +### 성능 개선 지원 +- 선응 개선 가이드 +- 성능 개선 지원 + +### 페이지 로드 이후 +- 웹이 복잡해지면서 로드보다는 유저와의 Interaction이 중요해짐 +- 페이지 전환이 많은 블로그같은 애플리케이션 +- 페이지에 머무르면서 다양한 동작을 표현하는 지도와 같은 애플리케이션 + +## 웹 반응성 +- 클릭, 탭, 키 입력과 같은 사용자 인터페이스와 상호작용하는 속도를 의미함 + +### 웹 반응성 현황 +- **70%** 사용자가 일주일에 한 번 이상 끔찍한 반응성을 경험 +- 반응성이 나쁜 페이지보다 좋은 페이지 사용량이 **2배 이상** + +### RAIL 모델 Response 성능 목표 +- 사용자 입력으로부터 100ms 이내 반응 + +## 웹 반응성 지표 +- TBT: Total Blocking Time: 원활한 상호작용 +- INP: Interaction to Next Paint: 빠른 피드백 +- CLS: Cumulative Layout Shift: 시각적 안정성 + +### Total Blocking Time +- Long Task: 메인 스레드에서 50ms 이상 실행되는 작업 +- Blocking Time: Long Task 중 50ms를 제외한 메인 스레드 점유 시간 +- TBT = SUM(Long Task - 50ms) + +### Interaction to Next Paint +- 사용자 입력이 발생하고 화면의 변화가 생길때까지의 시간 +- Web Vital 수치 중 최근에 추가한 수치 + +### Cumulative Layout Shift +- 페이지 전체 수명동안 발생하는 예기치 않은 레이아웃 이동 점수 + +## 웹 반응성 측정 방법 +- Lighthouse: 특정 환경에서 로드 성능을 포함한 다양한 성능을 측정 +- **Userflow**기능이 추가되어 로드 성능 뿐만 아니라 다른 성느옫 측정 가능 + +### Lighthouse Userflow +- Navigation: 단일 페이지의 로드 성능 측정 +- Snapshot: 사용자 인터렉션 후 페이지의 상태 측정 +- Timespan: 임의의 시간동안 사용자 인터렉션 측정 + +#### Navigation +- 단일 페이지 로드 분석 +- LCP와 Speed Index와 같은 페이지 로드 성능 점수 제공 +- 카테고리 + - Performance + - Accessibility + - Best Practice + - SEO + - PWA + +#### Snapshot +- 사용자 인터렉션 이후 특정 상태의 페이지 분석 +- SPA나 복잡한 폼의 접근성 이슈 확인 +- 카테고리 + - Performance + - Accessibility + - Best Practice + - SEO + +#### Timespan +- 임의의 시간동안 사용자 인터렉션 분석 +- 수명이 긴 페이지, SPA에서 성능 개선 포인트 제공 +- 카테고리 + - Performance + - Best Practice + - SEO + +##### Puppeteer로 Timespan 측정하기 +```JavaScript +import puppeteer from 'puppeteer'; +import { startFlow } from 'lighthouse/lighthouse-core/fraggle-rock/api.js'; + +const browser = await puppeteer.launch({ headless: false }); +const page = await browser.newPage(); + +const flow = await startFlow(page, { name: 'daum' }); +await flow.startTimespan({ stepName: 'routing' }); + +// 아래와 같이 UI 시나리오를 매번 정의하는 것이 매우 불편함 +await page.goto('https://m.daum.net'); +await page.click('#mArticle > nav:nth-child(2) > li.news1 > a > span'); +await page.click('#mArticle > nav:nth-child(2) > li.sports > a > span'); +await page.click('#mArticle > nav:nth-child(2) > li.enter > a > span'); + +await flow.endTimespan(); +await browser.close(); + +const report = await flow.generateReport(); +const json = await flow.createFlowResult(); +``` + +##### Chrome Devtools Recoder with Puppeteer +- Recorder 기능으로 화면을 기록하고 JSON으로 추출 +- Puppeteer, Cypress, Nightwatch 등에서 적용 가능 +```JavaScript +import puppeteer from 'puppeteer'; +import { startFlow } from 'lighthouse/lighthouse-core/fraggle-rock/api.js'; +import { createRunner, PuppeteerRunnerExtension } from '@puppeteer/replay'; + +const browser = await puppeteer.launch({ headless: false }); +const page = await browser.newPage(); + +const flow = await startFlow(page, { name: 'daum' }); +await flow.startTimespan({ stepName: 'routing' }); + +const runner = await createRunner( + readJSON('./recording.json'), + new PuppeteerRunnerExtension(browser, page) +); +await runner.run(); + +await flow.endTimespan(); +await browser.close(); + +const report = await flow.generateReport(); +const json = await flow.createFlowResult(); +``` + +## 웹 반응성 개선 사례 + +### TBT +- 챗본 주문 +- requestAnimationFrame() + +### INP +- Kakao 내부에서 제공하는 모티터링 시스템. WPM(Web Performance Monitoring) +- React의 상태 업데이트 2유형. 긴급 업데이트 & 전환 업데이트 + - 긴급 업데이트: 사용자가 즉각적으로 반응하길 바라는 변경사항 + - 전환 업데이트: 사용자가 업데이트 지연을 미리 인지하는 변경사항 +- v18 업그레이드 & startTransition() + - 늦게 업데이트해도 되는 UI는 startTransition()을 사용하여 지연된 업데이트를 하도록 수정 + +### CLS +- CLS를 발생하는 영역에 미리 Height를 지정하여 영역을 확보함 + +## Recap +- 로드 성능은 여전히 중요하지만 반응성도 점점 중요해진다 +- Lighthouse userflow로 측정하는 반응성 +- 반응성 지표별 개선 사례 + - 블로킹 타임 발생 시 강제 리플로우 확인 + - 전체 화면 업데이트 발생 시 중요한 부분 우선 업데이트 진행 + - 레이아웃 시프트는 간단한 작업만으로 쉽게 개선 가능 + +## Next Step +- 웹 반응성 측정 자동화 +- 웹 반응성 측정과 e2e 테스트 통합 +- 웹 반응성 개선 가이드 diff --git "a/Kakao/2022/frontend/03_\353\270\214\353\237\260\354\271\230_FE_\354\213\254\355\217\220\354\206\214\354\203\235\354\210\240.md" "b/Kakao/2022/frontend/03_\353\270\214\353\237\260\354\271\230_FE_\354\213\254\355\217\220\354\206\214\354\203\235\354\210\240.md" new file mode 100644 index 0000000..3603dfd --- /dev/null +++ "b/Kakao/2022/frontend/03_\353\270\214\353\237\260\354\271\230_FE_\354\213\254\355\217\220\354\206\214\354\203\235\354\210\240.md" @@ -0,0 +1,125 @@ +# 브런치 FE 심폐소생술 (feat. 신입 개발자의 우당탕탕 현업 적응기) +- [Youtube](https://www.youtube.com/watch?v=tDZ8xbO5OAc) +- [발표자료](https://speakerdeck.com/kakao/beureonci-fe-simpyesosaengsul) + +## 목차 +- 신입 개발자가 119 부른 썰 +- 심폐소생술 시행 +- 차근 차근 재활치료 +- 세션 마무리 + +## 신입 개발자가 119 부른 썰 +- 브런치 FE 담당자가 되다 + +### 브런치 FE 탐방하다 +- 코드에 담긴 간절한 기도 주문 +- 2015년에 작성된 TODO 주석 + +#### 브런치 프로파일 +- 이름: 브런치 FE +- 출생일: 2014 +- 사용 언어: ES3 +- 취미: jQuery +- 즐겨 읽는 책: Spring(Velocity, Handlebars) +- 좋아하는 음식: Grunt +- 특이사항: 서버와 함께 같은 집에서 동거 중 + +### 브런치 운영 이슈를 만나다 +- 사용자 행동 수집 오류 대응 +- 모바일 UI 미리보기 개선 +- 댓글 입력 오류 대응 + +#### 사용자 행동 수집 오류 대응 +- ES3, prototype, jQuery에 의존 + - JS 파일 간 import, export 불가 +- 거대한 전역 객체 window.B + - 1,000개 이상 x.prototype.x 모듈 + +```JavaScript +window.B.Util = { + setOnError: function() {}, + ajax: function() {} +}; + +var AbstractObject = function() {}; +AbstractObject.prototype = {}; + +var Top = function() {}.inherit(AbstractObject); +``` + +#### 모바일 UI 미리보기 개선 +- 빌드 시스템 - Grunt + - 개발환경에 따른 번들 경로 분기 + - 복잡한 빌드 설정 +- 코드 리뷰 + - 코드에 일관된 린트 설정이 적용되어 있지 않음 + - 지저분한 files changed + - 리뷰 시, 수시로 코드의 컨벤션에 대한 충돌이 발생 + +#### 댓글 입력 오류 대응 +- IndexSizeError 발생 + - 1400 줄 코드라인 + - init 콜백 지옥, props drilling + - 심상치 않은 entry, jQuery를 사용하여 에디터 객체 생성 +- 작업으로 인해 예상하지 못한 곳에서 Side Effect 발생 + +### 119 호출 + +#### 문제 정리 +- 코드 히스토리 부재 +- 오래된 개발환경 +- 복잡한 빌드 시스템 +- 코드 컨벤션 +- 구조화되지 않은 코드 +- Side Effect + +## 심폐소생술 시행 + +### 빌드 시스템 문제 +- 빌드 시스템 개선 -> Webpack 전환 +- ES6 작성 환경 구성 +- 빌드 설정 개선 +- 최신 테스트 / 모니터링 / 린트도구 도입을 위한 환경 구성 +- High Risk + - 테스트 코드 X, 수동 테스트 + +#### Webpack 전환 사이드 이펙트 해결 사례 +- CSS 번들링 설정 오류 - 이미지 누락 + - Jest Snapshot Test 도입하여 작업 전후 Snapshot 비교 + +### 오래된 개발환경 +- import / export 도입 +- 기능 단위 모듈 구조 도입 + +### 구조화되지 않은 코드 +- page class 컨벤션 +- Page: 페이지의 상태 / DOM 이벤트 +- Logic: 순수한 로직 +- Render: 순수한 렌더링 기능 구현 + +## 차근 차근 재활치료 + +### Side Effect +- 사내 모니터링 툴과 주변의 제보 +- 테스트 코드 도입, Cypress & Jest +- Page: Cypress E2E Test +- Logic: Jest Unit Test +- Render: Jest Snapshot Test +- 모니터링 툴 개선 + +### 코드 컨벤션 +- Brunch JavaScript Style Guide + +#### 레거시 코드에 ESLint 적용 사이드 이펙트 +- `==`과 `===`의 차이로 인한 기존 코드의 의도와 다르게 동작함 => ESLint 분기 적용 + +### 코드 히스토리 부재 +- 히스토리 남기기 +- 작업 내용 문서화 + +## 브런치 FE의 Next Item +- 디자인패턴: MVC, MVI 등의 도입 검토를 위한 프로토타이핑 +- 상태관리: 기존의 B.x 전역객체의 정보를 관리하기 위한 vanillaJs 상태 관리 +- 점진적 리팩토링: 신규 프로젝트 / 개편 페이지 대상으로 점진적 리팩토링 진행 중 +- 테스트 자동화: 주기적 테스트 실행을 통한 안정성 향상 + - Jenkins, Docker를 활용한 전체 테스트 자동 실행 및 리포팅 기능 개발 중 diff --git a/Kakao/2022/frontend/04_react-query.md b/Kakao/2022/frontend/04_react-query.md new file mode 100644 index 0000000..9ec2e3d --- /dev/null +++ b/Kakao/2022/frontend/04_react-query.md @@ -0,0 +1,426 @@ +# 눈에 보이지 않는 개선: My구독의 Redux에서 React-Query 전환 경험 공유 +- [Youtube](https://www.youtube.com/watch?v=YTDopBR-7Ac) +- [발표자료](https://speakerdeck.com/kakao/nune-boiji-anhneun-gaeseon-mygudogyi-reduxeseo-react-query-jeonhwan-gyeongheom-gongyu) + +## 보이지 않는 개선? +- 초기 개발에는 기존 설계에 맞춰서 개발 +- 출시 이후에는 새로운 기능, 사용자 경험 개선, 새로운 기능 등으로 인해 점점 복잡해짐 +- 완벽한 설계는 없고, 시간은 부족하다 +- 리팩토링으로 품질 개선을 해야 한다 + +## 목차 +- React-Query 조금 알아보기 +- Redux에서 React-Query로 전환 이유 +- React-Query로 전환 과정 +- 정리 + +## React-Query 조금 알아보기 +- API 요청 시 필요한 기능 + - Data fetching + - Cache + - Data Sync + +### React로만 구현하기 +- Context API, useState, useCallback, useEffect 등 많은 요소를 사용해서 구현 + +#### Data fetching +```JavaScript +const [data, setData] = useState(null); +const fetch = useCallback(() => {}, []); +useEffect(() => { fetch(); }, []); +return { data, refetch: fetch } +``` + +#### Cache +```JavaScript +const CacheContext = React.createContext(null); +const { Provider } = CacheContext; +function CacheProvider({ children }) { + const [cache, setCache] = useState({}); + // ... + return {children} +} +``` + +#### Data Sync +```JavaScript +useEffect(() => { + const interval = setInterval(handleFocus, cacheTime); + window.addEventListener('focus', handleFocus, false); + // ... + + return () => { + clearInterval(interval); + window.removeEventListener('focus', handleFocus, false); + // ... + }; +}, []); +``` + +### Reqct-Query를 사용했을 때 +- QueryClient(Context), useQuery, useMutation으로 해결 가능 + +### React-Query 없이 React 자체만으로는 불가능하나요? +- React에서 제공하는 기본 Hook으로도 충분히 멋진 비동기 커스텀 훅을 만들 수 있습니다 +- 필요한 기능만 구현할 수 있으며 별도의 라이브러리 설치가 필요 없습니다 +- 대신, 기본 Hook으로 만들기 위해서는 시간과 노력이 필요합니다 +- React-Query 대비 안정성을 확보하기 어렵습니다 + +## Redux에서 React-Query로 전환 이유 +- 구조 +- 데이터 +- 에러 + +### 구조 +- Redux + Redux-Saga +- Redux 자체는 비동기 처리를 목적으로 나온 라이브러리가 아님 +- Redux-Saga를 사용하여 비동기 처리를 하고 데이터를 전달해야 함 +- Redux-Saga로 비동기 처리를 하는 경우에는 로드 상태 관리를 하는 모듈을 만들어야 함 +- React-Query 내부에서 로드 상태를 관리하고 있음 + +#### Redux +```JavaScript +export function useData({ ...state }) { + useEffect(() => { + actions.fetch구독정보API(); + }, []); + const { 구독정보 } = state; + + return 구독정보; +}; +``` + +#### React-Query +```JavaScript +export function useData() { + const { data, isLoading } = useQuery('KEY1', fetch상품정보API); + const { data: 구독정보 } = useQuery( + 'KEY2', + fetch구독정보API(data), + { enabled: !isLoading }, + ); + + return 구독정보; +}; +``` + +### 데이터 +- 클라이언트 데이터와 서버 데이터의 구분 +- 클라이언트 데이터는 Context API +- 서버 데이터는 React-Query + +### 에러 + +#### Redux에서의 에러 핸들링 +- 결제정보 API -> 에러발생 -> loadStatus -> re-render -> 컴포넌트 -> onError 실행 +```JavaScript +useEffect(() => { + actions.fetch결제정보(); +}, []); + +useEffect(() => { + if (loadStatus?.fetch결제정보?.error) { + actions.fetching('fetch결제정보'); + history.replace('/'); + } +}, [loadStatus?.fetch결제정보?.error]); +``` + +#### React-Query에서의 에러 핸들링 +- 결제정보 API Query -> 에러발생 -> onError 실행 + +### Why? 다른 라이브러리 대신 React-Query를 사용하는 이유가 있나요? +- RTK-Query + - Redux 구조를 그대로 사용해야 합니다 +- Apollo + - 스키마를 정의해야 합니다 +- SWR + - enabled 옵션을 제공하지 않습니다 +- Redux에서 API 상태에 따라 화면을 구성하기 위해서는 **별도의 도구나 상태가 필요**합니다 +- Redux-Saga는 **의존성이 깊은 구조**를 만들어 낼 수도 있습니다 +- Redux는 간단한 API 추가에도 **장황한 Bolierplate가 필요**합니다 +- Redux는 API 에러 핸들링 과정에서 다소 불필요한 작업**이 발생할 수 있습니다 +- 우수한 비동기 처리 라이브러리가 많이 있습니다 +- 사용하는 **방식, 구조, 기능**에서 React-Query가 더 적합하다고 판단하여 사용하게 되었습니다. + +## React-Query로 전환 과정 + +### 기존 My 구독 +- Redux 고차 컴포넌트 +- Props drilling +- Production + +### 전환 과정 +- Redux 고차 컴포넌트 -> Redux Hook +- Redux Hook -> React-Query +- React 16 -> React 18 + +#### Redux 고차 컴포넌트 -> Redux Hook +- 불필요한 re-render 발생할 수 있음 +- 전체적인 코드 구조 변경 - API & 로직 누락 발생할 수 있음 + +##### Redux 고차 컴포넌트 예시 +```JavaScript +function 상품상세페이지({ 액션, ...상태 }) { + useEffect(() => { + 액션.fetch구독데이터({ productGroupId }); + }, [productGroupId]); + + useEffect(() => { + if (loadStatus?.fetch구독데이터?.error) { + error(); + } + }, [loadStatus?.fetch구독데이터?.error]); + + const [ 구독데이터, loadStatus ] = 상태; + const fetched = isFetched(deps, loadStatus); + + return fetched && ; +} + +export default connectStore(상품상세페이지); +``` + +##### Redux Hook 예시 +```JavaScript +export default function 상품상세페이지() { + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(액션.fetch구독데이터({ productGroupId })); + }, [productGroupId]); + + useEffect(() => { + if (loadStatus?.fetch구독데이터?.error) { + error(); + } + }, [loadStatus?.fetch구독데이터?.error]); + + const 구독데이터 = useSelector(({ 구독데이터 }) => 구독데이터); + const loadStatus = useSelector(({ loadStatus }) => loadStatus); + const fetched = isFetched(deps, loadStatus); + + return fetched && ; +} +``` + +##### Redux Hook 최적화 +- loadStatus 변경에 따라 의도하지 않은 re-render가 발생하는 컴포넌트가 있음 +- Redux Hook을 최적화 하는 이유 + - 전체적인 구조가 변경됨에 따라 언제 작업이 마무리될지 모릅니다 + - 작업 도중 다른 작업이 발생할 수 있습니다 + - 해결할 수 있는 부분을 놔두기가 애매합니다 +```JavaScript +export default function useLoadStatus(deps = []) { + return useSelector( + ({ loadStatus }) => deps.reduce((status, key) => ({ + ...status, + [key]: loadStatus[key], + }), {}), + isEqual, + ); +}; +``` +```JavaScript +const loadStatus = useLoadStatus(['fetch사용자데이터', 'fetch매대데이터', 'fetch상품데이터']); +``` + +#### Redux Hook -> React-Query + +##### React-Query 예시 +```JavaScript +export default function 상품상세페이지() { + const { data: 구독데이터, isLoading } = useQuery( + ['구독데이터'], + () => fetch구독데이터(productGroupId), + { + enabled: productGroupId, + onError: () => { + error(); + }, + }, + ); + + // ... + if (!매대Success || !상품Success || (!이용권Idle && !이용권Success) || 일시중지상품Loading) { + return null; + } + + return !isLoading && ; +} +``` + +##### React-Query 개선 - useQueriesLoading +```JavaScript +export function useQueriesLoading() { + const client = useQueryClient(); + const queries = cleint.getQueryCache().findAll(); + + const [queryChangedCount, setQueryChangedCount] = useState(0); + + useEffect(() => { + if (queries) { + setQueryChangedCount(queries.length) + } + }, [queries]); + + useEffect(() => { + if (queries.every(({ state }) => state.status !== 'loading') && queryChangedCount >= 0) { + setQueryChangedCount((prevState) => prevState - 1); + } + }); + + return queryChangedCount >= 0; +} +``` +```JavaScript +export default function 상품상세페이지() { + const { data: 구독데이터, isLoading } = useQuery( + ['구독데이터'], + () => fetch구독데이터(productGroupId), + { + enabled: productGroupId, + onError: () => { + error(); + }, + }, + ); + + // ... + const isQueriesLoading = useQueriesLoading(); + + if (isQueriesLoading) { + return null; + } + + return !isLoading && ; +} +``` + +##### 개선 중에 먼저 나와버린 새로운 기능 +- 충돌을 해결하기 위해 5가지를 고민했습니다 +- Query 작성 방식 +- Query Mutation 네이밍 +- 폴더 구조 개선 +- 프로젝트 기본 옵션 +- Query key 작성 규칙 + +> Query 작성 방식과 폴더 구조 개선만 발표에서 다룰 예정 + +###### Query 작성 방식 +- 각각 컴포넌트 내에서 다양한 API를 호출하며 페이지마다 API 호출 조건이 다름 + - 이로 인해서 API 변경이 있을 경우 사용하느 모든 컴포넌트를 수정해야 함 +- 기본 Query 개념 도입, API와 1:1 관계 +- 페이지 단위 Key값 추가 => 페이지 단위 쿼리 초기화 가능 +- Select에서 실질적인 데이터가 있는 값만 반환 => 쿼리를 사용할 때 중복 Select 방지 +- onError 옵션이 있어야 useErrorBoundary를 설정하도록 처리 => 에러 핸들링 누락 방지 +```JavaScript +import { useQuery as useQueryOrigin } from 'react-query'; + +export default useQuery(queryKey, queryFn, options = {}) { + const { /*...*/ } = options; + + return useQueryOrigin(queryKey, queryFn, { /* ... */ }); +} +``` + +###### 폴더 구조 개선 +- 목적에 따른 폴더 분류 +- GET => queries +- POST, PUT, DELETE => mutations + +#### React 16 -> React 18 +- IE 지원을 위해서 React 18을 올리지 않고 있었음 +- IE 지원 중단으로 인하여 React 18로 올리면서 최신 버전의 라이브러리들을 사용할 수 있게 됨 + +##### React 16 예시 +```JavaScript +function 상품상세페이지() { + // ... + const { isSuccess } = useQuery(/* ... */); + const { isSuccess: 구독정보Success } = useQuery(/* ... */); + const isRender = isSuccess && 구독정보Success; + + return isRender && <상품상세레이아웃 />; +} +``` +```JavaScript +function 상품상세레이아웃() { + // ... + const { data } = useQuery(/* ... */); + return ; +} +``` + +##### React 18 예시 +```JavaScript +function Route() { + // ... + return <상품상세페이지 />; +} +``` +```JavaScript +function 상품상세페이지() { + // ... + const { data } = useQuery(/* ... */); + return ; +} +``` + +### 전환 과정 정리 +- **My구독 특성과 상황 덕분에** 총 3단계로 나누어 전환작업을 진행했습니다 +- 과정마다 그 상황에 맞춰 최대한 더 좋은 방향으로 진행할 수 있도록 여러 도전을 진행했습니다 + - Redux Hook 기반으로 전환할 때 **select를 좀 더 최적화** 하는 방법에 대해 공유했습니다 + - React-Query로 전환하는 과정에서 React 16에서 **상태 지옥에서 벗어나는 방법**에 대해 공유했습니다 + - React-Query를 **어떻게, 어떤 구조로 사용하는지 공유**했습니다 + - React 18로 전환하면서 **어떤 이점이 발생하는지 공유**했습니다 + +## 정리 +- 그래서 써보니 어떤가요? + +### 이런 점에서는 좋았습니다 +- **Hook 기반**을 제공하고 **캐싱을 지원**합니다 +- 비동기 처리를 위한 **유용한 도구**를 제공합니다 + - 비동기 처리 상태: isLoading, isFetching, isSuccess 등 + - Infinite Queries +- **백그라운드 패칭**을 지원합니다 + - 서버 데이터가 자주 변경되더라도 지속적으로 업데이트가 가능합니다 + - focus, mount, interval 등 + +### 이런 상황에서는 고민을 해보세요 +- 서버 사이드 데이터가 거의 없는 경우 => **서버 사이드 데이터가 더 많아질 때 적용** + - recoil, redux +- React 18에서 비동기 처리 상태때문에 도입하는 경우 => Suspense 사용 + +### 이런 아쉬운 점이 있습니다 +- Mutation은 한 번만 호출하기 위해 **Wrapper 함수가 필요**합니다 + - Redux Saga의 takeLatest +```JavaScript +function useExcuetionOnce(func, delay) { + // ... + + return async (...args) => { + if (!comleteRef.current) { + return; + } + + comleteRef.current = false; + try { + await func(...args); + await waitMutating(); // useIsMutating 기반 mutation 작업 대기 + } catch (e) { + throw e; + } + }; +} +``` +- UI 테스트를 진행하게 될 떄 **Mock API**가 필요할 수 있습니다 + - Storybook 기반 E2E Test와 Snapshot Test 진행 중 + - Mock Service Worker를 사용하여 해결 중 +- 항상 서버 데이터와 같은 데이터를 바라보는 것이 좋은 것은 아닙니다 + - 구독 완료 페이지는 1회성 페이지, 재요청이 발생 시 데이터가 빈값이 오기에 에러가 발생함 + - `refetchOnWindowFocus: false`, `retry: 0`과 같이 옵션을 해제해야 함 +- 전체적인 데이터 흐름을 파악하기 어렵다는 느낌을 받기도 합니다 + - Pure 컴포넌트와 상태 컴포넌트를 잘 구분하는 것이 중요 + - Pure 컴포넌트: API 데이터 중 하나만 필요 + - 상태 컴포넌트: Props drilling diff --git "a/Kakao/2022/frontend/05_\352\264\221\352\263\240_\354\233\271_SDK.md" "b/Kakao/2022/frontend/05_\352\264\221\352\263\240_\354\233\271_SDK.md" new file mode 100644 index 0000000..ce426e1 --- /dev/null +++ "b/Kakao/2022/frontend/05_\352\264\221\352\263\240_\354\233\271_SDK.md" @@ -0,0 +1,266 @@ +# 광고 웹 SDK 개편기 with Preact +- [Youtube](https://www.youtube.com/watch?v=s0hJjmPlNkU) +- [발표자료](https://speakerdeck.com/kakao/gwanggo-web-sdk-gaepyeongi-with-preact) + +## 목차 +- 광고 웹 SDK 소개 +- 개편 목표 +- 개편 톺아보기 +- Preact 도입 +- 개편 그 이후 + +## 광고 웹 SDK 소개 +- 쇼핑몰이나 블로그와 같은 매체에 광고를 게재하고 수익을 창출하기 위해 제공되는 **웹 스크립트** +- AdFit SDK + +### 광고 단위란? +- 광고를 노출할 영역 + +### 광고 요청/응답 과정 +- 광고 웹 SDK <--광고 소재--> 광고 서버 +- iframe으로 격리된 소재 코드 + +### 광고 웹 SDK의 주요 기능 +- 광고 소재 렌더링 +- 광고 지표 수집 +- 어뷰징 방지 + +## 개편 목표 +- 변경에 유연하고 이해하기 쉬운 코드 +- SDK 프로젝트의 통합 +- 테스트 강화 +- 최신 기술의 도입 + +### 변경에 유연하고 이해하기 쉬운 코드 +- 하나의 모듈에 연관된 많은 기능들 +- SOLID Principles + - SRP: 단일 책임 원칙 + - OCP: 개방-폐쇄 원칙 + - LSP: 리스코프 치환 원칙 + - ISP: 인터페이스 분리 원칙 + - DIP: 의존성 역전 원칙 + +### SDK 프로젝트의 통합 +- 비공개/공개 SDK 내에 공통 코드 존재 + - 유지보수 비용 증가 + +### 테스트 강화 +- Unit: 커버리지 90%이상 +- E2E: 다양한 브라우저 대응 + +### 최신 기술의 도입 +- IE 지원 중단으로 인한 레거시 코드 제거 가능 +- Typescript + Preact + +## 개편 톺아보기 +- 단일 책임 원칙에 충실한 광고 도메인 모델 +- 의존관계에 따른 계층 구조 +- 렌더링 프로세스 +- SDK 프로젝트의 통합 +- Unit Test + +### 단일 책임 원칙에 충실한 광고 도메인 모델 +- 하나의 객체에 모든 기능을 담게 된다면? + - 코드 복잡도 증가 + - 협업 시 소스 코드 충돌 +- 광고 업무 용어를 기준으로 한 도메인 모델 분리 + +### 의존관계에 따른 계층 구조 +- 저수준 모듈 & 고수준 모듈 계층 분리 + - 개방-폐쇄 원칙(OCP), 기능의 확장은 쉽게, 구조의 변경은 최소화 +- 저수준 모듈 -> 고수준 모듈. 단방향으로 흐르는 모습으로 설계 + +#### 광고 소재 스토어 추가 +- abstract class `광고 소재 스토어`: `광고 단위`를 전달하고 `광고 데이터`를 응답 받는 중개자 +- interface `광고 서버`: `광고 소재 스토어`로부터 `광고 단위`를 받아 `광고 데이터`를 전달 +- abstract class `광고 소재`: `광고 소재 스토어`에서 `광고 데이터`로 생성한 광고 +- 인터페이스 또는 추상 클래스로 만든 이유는 의존성 역전 원칙(DIP)를 지키기 위해서 + +#### 의존성 역전 원칙(DIP)의 적용 +- 광고 서버 인터페이스와 광고 서버 구현체를 나눔으로써 테스트 시에도 테스트용 구현제를 주입하여 단순해짐 + +### 렌더링 프로세스 + +#### 광고 렌더링에 필요한 기능들 +- 소재 렌더링 +- 렌더링 검증 +- 지표 수집 +- 등... +- 각각의 함수를 나눠서 구현하기로 결정함 + +#### 렌더러 함수와 렌더링 파이프라인 +- 렌더링 파이프라인: 렌더러 함수라는 인터페이스를 사용할 뿐 기능에 의존하지 않음 +- 렌더러 함수 interface: 기능을 구현할 수 있는 함수 + +### SDK 프로젝트의 통합 +- 비공개 SDK 빌드 진입점 +```JavaScript +export default new 비공개_SDK( + new 렌더링_파이프라인([ + 비공개_소재_렌더링_함수, + 비공개_렌더링_검증_함수, + 비공개_지표_수집_함수, + ]), +); +``` +- 공개 SDK 빌드 진입점 +```JavaScript +export default new 비공개_SDK( + new 렌더링_파이프라인([ + 공개_소재_렌더링_함수, + 공개_렌더링_검증_함수, + 공개_지표_수집_함수, + ]), +); +``` + +### Unit Test +- 유닛 테스트 커버리지 목표 90% + - 유닛으로 다루기 힘든 부분은 E2E로 커버 + - Storybook을 활용하여 커버 +- Github & Jenkins를 사용한 자동화된 유닛 테스트 커버리지 검사 +- Jest 커버리지 검사 설정 +```JavaScript +module.exports = { + // ... + coverageThreshold: { + global: { + branches: 90, + functions: 90, + lines: 90, + statements: 90, + } + }, + // ... +}; +``` + +#### 의존성 있는 함수 foo 예제 +```JavaScript +import { bar } from './bar'; + +export function foo() { + const barResult = bar(); + return `if(kakao)${barResult}`; +} +``` +```JavaScript +import { foo } from './foo'; + +test('foo', () => { + jest.mock('./bar', () => { + bar: jest.fn(() => 2022), + }); + expect(foo()).toBe('if(kakao)2022'); +}); +``` + +#### 의존성 주입을 사용한 함수 foo 예제 +```TypeScript +import { Baz } from './Baz'; + +// 고차함수로 설계 변경 +export function foo(bar: Baz) { + return () => { + const barResult = bar(); + return `if(kakao)${barResult}`; + }; +} +``` +```JavaScript +import {} from './foo'; + +test('foo', () => { + const barMock = () => 2022; + const fooWithBarMock = foo(barMock); + expect(fooWithBarMock()).toBe('if(kakao)2022'); +}); +``` + +## Preact 도입 +- 처음 광고 소재 렌더링을 설계할 때는 렌더러 함수로만 구현하려고 했음 +- 충분히 가능한 일이고, 설계시 의도한 부분이었음 +- 하지만, DOM요소를 사용하려면 아래와 같은 절차적인 코드가 필요했음 +```JavaScript +const parent = document.createElement('div'); +const child = document.createElement('p'); + +child.innerText = 'Hello, World!'; +parent.appendChild(child); +return parent; +``` +- React에서 제공하는 JSX 기능을 사용하면 개발이 쉬워질 것으로 판단함 +```JavaScript +return ( +
+

Hello, World!

+
+); +``` + +### 리액트 컴포넌트를 사용한다면? +- 함수와 JSX만으로 컴포넌트를 만들 수 있는 React에 대해서 고민하기 시작함 + +### 광고의 렌더링을 리액트 컴포넌트로 표현하면? +- DOM 요소의 렌더링을 document.createElement 등으로 생성하거나 별도의 템플릿을 다룰 필요 없이 JSX 문법을 통해 간결한 표현이 가능 +- 컴포넌트를 계층 구조로 나눠 복잡한 기능을 작은 컴포넌트로 분산 +- 리액트 훅을 사용하면 컴포넌트 로직과 라이프사이클의 관리가 용이 +- 리액트 컴포넌트에 익숙한 개발자가 많음 +- => 광고 SDK 내에서 UI 관련된 부분이 적지만, 얻을 수 있는 장점이 많다고 판단함 + +> 리액트를 쓰자니 빌드된 파일의 크기가 커질 것 같은데? +> 3KB, Preact 공식 홈페이지에서 소개하는 크기 + +### Preact 렌더러 함수 구현 +```JavaScript +import { render } from 'preact'; + +export function preact_렌더러_함수(광고소재) { + render( + , + element, + ); +} +``` +```JavaScript +export default new 비공개_SDK( + new 렌더링_파이프라인([ + preact_렌더러_함수, // preact로 만든 렌더러 함수 + 공개_렌더링_검증_함수, + 공개_지표_수집_함수, + ]), +); +``` + +### 구현 방법의 풍부함 +- DOM 요소로 표현되네? => Component +- 컴포넌트 라이프사이클 또는 상태 로직이 필요해! => Hooks +- 컴포넌트로 구현 안해도 되는 독립적인 기능이야! => Renderers + +### Storybook의 활용 +- SDK의 렌더링을 확인하고 테스트 할 수 있게 됨 +- SDK가 실환경 테스트에서 겪는 어려움 + - Unit 테스트를 통해 어느정도 기능이 동작함을 보장할 수 있으나, 실제 동작하는 모습을 눈으로 확인하기가 어려움 + +### Storybook이란? +- 컴포넌트를 독립적으로 테스트하며 개발할 수 있도록 도와주는 개발툴 +- React, Vue, Svelte부터 Preact까지 다양한 프레임워크 지원 +- Unit Test: 자동화 테스트 + 실시간 개발 +- E2E Test: 자동화 테스트 + 무겁지만 다양한 브라우저 환경 +- **Storybook: 실시간 개발 + 가벼운 실제 브라우저 환경** + +## 개편 그 이후 + +### 순환 복잡도 +- 코드의 분기로 인한 경로 개수를 측정해 복잡성을 나타내는 정량적 지표 +- 20 또는 25 이하를 유지하면 개발하는 것을 권장함 +- 아래 코드의 순환 복잡도 = `P(분기 개수) + 1` = `1 + 1` = 2 +```TypeScript +function isOdd(value: number) { + if (value % 2 === 1) { + return true; + } + + return false; +} +``` diff --git "a/Kakao/2022/frontend/06_\354\202\254\354\247\204\355\216\270\354\247\221\352\270\260.md" "b/Kakao/2022/frontend/06_\354\202\254\354\247\204\355\216\270\354\247\221\352\270\260.md" new file mode 100644 index 0000000..4b7ec18 --- /dev/null +++ "b/Kakao/2022/frontend/06_\354\202\254\354\247\204\355\216\270\354\247\221\352\270\260.md" @@ -0,0 +1,196 @@ +# 사진편집기! 이것DOM 적용해볼GL? +- [Youtube](https://www.youtube.com/watch?v=kI1Jh4_74Ac) +- [발표자료](https://speakerdeck.com/kakao/sajinpyeonjibgi-igeosdom-jeogyonghaebolgl) + +> NOTE: 발표자료와 영상을 보는 것을 추천. 발표 내용 자체가 상당히 복잡함 + +## 목차 +- 사진편집기에 WebGL 부어보기 +- DOM딱 DOM딱 아이템 붙이기 + +## 사진편집기에 WebGL 부어보기 + +### 사진편집기? +- Daum Cafe, Kakao Story, Brunch의 글쓰기에서 제공하는 사진 편집 기능 +- 자르기 / 크기 조절 +- 보정 / 필더 +- 텍스트 / 이미지 추가 + +### 왜 사진편집기에 WebGL을 부었을까? +- 사진 편집을 위해 서버 API를 사용했으나, API가 종료 +- OpenGL의 Web버전인 WebGL이 있음 + +### WebGL +- Web Graphics Library +- OpenGL ES 2.0 기반 +- GPU 사용 +- JS 개발 지원 +- 대부분의 모던 브라우저 지원(IE 11 지원) + +### WebGL 어떻게 사용하지? + +#### WebGL 동작 순서 +- 유저가 캔버스를 생성 +- 캔버스에서 WebGL 컨텍스트 생성 +- 유저 입력(V, F, T)에 따라 새로운 이미지를 만들고 캔버스에 전달 + - Vertext: 캔버스 내부 좌표 변환 + - Fragment: 캔버스 색칠 + - Texture: 쉐이더에서 접근할 수 있는 데이터 배열 +- 캔버스에서 새로운 이미지 렌더링 + +> 기존 API와 비교했을 때 성능적인 우위가 있다는 것을 확인하고, WebGL 사용을 결정함 + +### WebGL과 함께 하면서 무엇이 바뀌었을까요? +- 모자이크: API 사용시에는 최대 갯수가 제한(5개)이 있었지만, WebGL에서는 무제한 +- 블러: API 사용시에는 크기 조절만 가능, WebGL에서는 크기와 강도 조절도 가능 + +## DOM딱 DOM딱 아이템 붙이기 +- 사진 편집기는 캔버스위에 이미지를 그리는 방식으로 구현 +- DOM(편의성) VS Canvase(일관성) +- DOM + - 디버깅 난이도: **낮음** + - 이벤트 처리 난이도: **낮음** + - CSS 사이드 이펙트: 있음 + - History-Canvas 동기화: 있음 + - 공간 제약: 없음 +- Canvas + - 디버깅 난이도: 높음 + - 이벤트 처리 난이도: 높음 + - CSS 사이드 이펙트: **없음** + - History-Canvas 동기화: **없음** + - 공간 제약: 있음 +- DOM을 사용하는 경우 공간제약에 구애받지 않고 이벤트를 처리할 수 있음 => DOM 방식으로 결정 + +### 희노애락의 여정 + +#### 희 +```tsx +interface Props { + moveable: boolean; + resizable: boolean; + removeable: boolean; + retatable: boolean; + size?: Size; + zoom?: number; + degree?: number; + position?: Position; + fixRatio?: boolean; + hidden?: boolean; + onChange?: DecoratorOnChangeCallback; + removeCallback?: DecoratorOnChangeCallback; +} +const Decorator = (props: Props) => { + // ...states + // ...events + // ...react hooks + + return ( + + + {children} + + + + + + + + ); +} + +export default Decorator; +``` +```tsx +const ItemComponent = () => { + const [state, setState] = useState(); + + return ( + + Good + + ); +} +``` + +> 너무 과하지 않나? + +##### 초기 설계 +- 메뉴에서 아이템 추가/삭제 +- 히스토리 변경과 preview에 알림 +- preview에 작성한 훅을 통해 변경사항 감지 +- 아이템을 렌더링 후 수정시 히스토리에 알림을 줌 + +###### 단점 +- 코드 중복 +```tsx +const Item = (props: Props) => { + const dispatch = useDispatch(); + const [state, setState] = useState(); + + const onMoveCallback = useCallback(() => { /* ... */}, []); + const onResizeCallback = useCallback(() => { /* ... */}, []); + const onRemoveCallback = useCallback(() => { /* ... */}, []); + const onRotateCallback = useCallback(() => { /* ... */}, []); + + return ( + + + + {/* ... 각종 핸들러 ... */} + + + ); +}; + +export default Item; +``` +- 관리 중복: 히스토리와 메뉴에서 동시에 관리하고 있었음 + +###### 단점 개선 +- 중복된 콜백을 데코레이터 안으로 이동 +```tsx +const Decorator = (props: Props) => { + const [box, setBox] = useState(); + + const onMoveCallback = useCallback(() => { /* ... */}, []); + const onResizeCallback = useCallback(() => { /* ... */}, []); + const onRemoveCallback = useCallback(() => { /* ... */}, []); + const onRotateCallback = useCallback(() => { /* ... */}, []); + + const onMoveStop = useCallback(() => { onChange(nextPosition); }, []); + const onResizeStop = useCallback(() => { onChange(nextSize); }, []); + const onRotateStop = useCallback(() => { onChange(nextAngle); }, []); + + return ( + + {children} + + {props.moveable && } + {props.resizeable && } + {props.removeable && } + {props.rotatable && } + + + ); +} + +export default Decorator; +``` + +#### 노애 +- 히스토리, 캔버스의 동기화 이슈 + - 복잡한 로직을 거쳐서 좌표를 계산하고 캔버스에 유사한 모습으로 처리해야 했음 +- CSS 사이드 이펙트 + - 자르기 기능 개발 중 발생 + - 편집 대상이 나닌 불투명해야 하는 부분과 편집을 해야 하는 부분의 계층을 분리해야 했는데, 계층 분리에서 이슈가 발생함 + +#### 락 +- 사진 편집기가 많은 관심을 받고 있는 것이 즐거움 + +## 참고 자료 +- https://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html diff --git "a/Kakao/2022/frontend/07_\354\275\224\353\223\234\352\260\200\353\217\205\354\204\261.md" "b/Kakao/2022/frontend/07_\354\275\224\353\223\234\352\260\200\353\217\205\354\204\261.md" new file mode 100644 index 0000000..21fe46a --- /dev/null +++ "b/Kakao/2022/frontend/07_\354\275\224\353\223\234\352\260\200\353\217\205\354\204\261.md" @@ -0,0 +1,364 @@ +# 섬세한 ISFP의 코드 가독성 개선 경험 +- [Youtube](https://www.youtube.com/watch?v=emGLxi0LvNI) +- [발표자료](https://speakerdeck.com/kakao/seomsehan-isfpyi-kodeu-gadogseong-gaeseon-gyeongheom) + +## 목차 +- 정확한 단어 고르기 +- 잘 보이는 형태로 작성해보기 + +## 정확한 단어 고르기 +- 다른 뜻을 가진 단어와 구분하기 +- 보다 구체적인 단어로 바꾸기 +- 정확하지 않아도 좋은 경우 + +### 다른 뜻을 가진 단어와 구분하기 +```JavaScript +// Before +// loadData => 특정 공간에 싣는 것(저장1)까지 완결 +// data => data에 가져옴(저장2) +// 저장 의미 중복 +const data = await loadData(); + +// After +// 저장의 의미가 1개 +const data = await fetchData(); +// 함수 내에서 저장까지 완료 후 성공/실패만 전달 +const success = await loadData(); +``` + +#### 다음 중 데이터가 없을 때 최초 한 번만 바뀌는 변수는? +```JavaScript +// React-Query 예제 +const { isLoading, isFetching } = useQuery(['todos'], fetchTodos); +``` +- isLoading: 데이터를 저장한 이후에는 다시 불러와도 상태가 바뀌지 않음 +- isFetching: 데이터를 불러올 때마다 바뀜 + +#### get? query? +```jsx +test('should show "Username"', () => { + render(); + // get - 가져오다 + // getByText - 결과가 없으면 에러 + expect(screen.getByText('Username')).toBeIntheDocumnet(); + // query - 질문하다 + // queryByText - 결과가 없으면 null + expect(screen.queryByText('Username')).toBeIntheDocumnet(); +}); +``` +- query: 결과는 없을수도 있지만 확인만 해볼 때 +- get: 가져올 대상은 필히 존재한다고 가정, 가져온 뒤에 대상을 활용할 목적이 있을 때 + +#### UI 컴포넌트 작성 - UI 명칭 이해하기 +- 컴포넌트 이름만 보더라도 구성을 예측할 수 있어야 한다 +- AppBar, Global Navigation Bar, Local Navigation Bar +- Card: 하나의 주제로 묶인 컨텐츠와 액션의 모든 것 +- Box: 내용물을 감싼 Wrapper의 개념 + +##### Before +```jsx +const FruitBox = (fruit) => { + return ( +
+ {fruit.name} + + +
+ ); +} +``` +##### After +```jsx +// 1안. 이름을 Card로 변경하기 +const FruitCard = (fruit) => {} +// 2안. 박스의 의미에 맞춰 분리하기 +const FruitBox = (children) => { + return ( +
+ {children} +
+ ); +} +``` + +#### 사례 1 +- 요청사항: 첫 번째 검색 결과가 항상 하이라이트되는 기능을 제거해주세요 +- select: 선택하다 => 하나의 결과를 항상 **선택**한다 +- search: 찾다 => 모든 결과를 찾는 것에 그친다 +- select에서는 선택을 강조하는 것이 자연스러움. select 컴포넌트를 바꾸는 것이 아니라, 비슷한 기능을 제공하지만 목적에 맞는 컴포넌트로 변경 +##### Before +```jsx +import { Select } from 'ui/Select'; + +export default function App() { + return ( +