헌집줄게, 새집다오 - 리액트 프로젝트 구조조정

10336b5650295a6d91a6ae90be63dc55?s=47 SoYoung210
October 29, 2019

헌집줄게, 새집다오 - 리액트 프로젝트 구조조정

기존에 사용하던 구조를 벗어나, 새로운 구조를 도입 했던 이유와 현재까지의 과정을 소개합니다.

10336b5650295a6d91a6ae90be63dc55?s=128

SoYoung210

October 29, 2019
Tweet

Transcript

  1. ೴૘઴ѱ ࢜૘׮য় ੉ࣗ৔]TPZPVOH!SBJOJTUDPN IUUQTTPTPEFW

  2. : , , . : . , . , .

    : FEConf( ) . , speakerdeck . ೴૘઴ѱ ࢜૘׮য় 스크립트 영역 스크립트 영역
  3. ੉ࢎܳ ೧ঠѷ׮! Nߣ੄ ੉ࢎҗ੿ਸ ѻਵݴ… য়ט׮ܙ੉ঠӝ જ਷ ૘੉঻؍, Legacy Architecture

  4. -FHBDZ"SDIJUFDUVSF જ਷૘੉঻؍

  5. 소비자가 선택하는 금융 !೐۽ં౟ҙ੼

  6. 소비자가 선택하는 금융 !೐۽ં౟ҙ੼

  7. 클라이언트는 제품의 정보가 수렴하는 곳. 프로젝트는 관리하기 어려워집니다. 이 모든

    카테고리가 수렴하는 곳이 클라이언트, 즉 웹프론트엔드 입니다. 이렇게 여러가지 서비스를 제공하는 하나의 프로젝트를 어떻게 구성할 것인가에 대한 저희팀의 첫번째 답은,
  8. %PNBJO ઺बਵ۽ࣗ೐౟ਝযܳࢸ҅ೞחѪ %PNBJO 그러나 DDD는 아닌..

  9. %PNBJO ઺बਵ۽ࣗ೐౟ਝযܳࢸ҅ೞחѪ %PNBJO ex : , , .. 그러나 DDD는

    아닌..
  10. -FHBDZ"SDIJUFDUVSF - .. 1SFTFOUBUJPO %BUB %PNBJO &OUJUZ Layer Layer use

    case Layer Layer -BZFSܻ࠙৬੄ઓࢿ઱ੑী'PDVT 전체 구조를 이렇게 4가지 레이어로 분리했습니다. 이 구조는 Layer의 분리와 의존성주입에 Focus를 맞추어 설계했습니다.
  11. -FHBDZ"SDIJUFDUVSF - .. 얼마전 발표했던 CleanArchitecture in Banksalad의 마지막 장표인데요,

    그 때 이 구조를 소개할 때 장점으로 ‘View는 Service만 알면 되고, 그 행동은 주입받은 리소스에 의존하기 때문에 이 규칙만 이해하면 빠르고 규칙적인 개발이 가능하다고 말씀드렸었습니다.
  12. -FHBDZ"SDIJUFDUVSF - .. 기존 구조는 이렇게 Context에서 apiProvider, service 등

    프로젝트에 필요한 요소를 모두 모아서 하나의 인스턴스로 생성하는 형태였습니다.
  13. Context - 대운동장 Presentation services repositories View - Presentation Layer

    - router, component 등 WJFX TFSWJDF core Service - Domain Layer http - Data Layer data Repository - Data Layer EBUBSFQPDPSFSFQPܳ*NQMFNFOUT DPOUFYUTFSWJDFTחDPSF੄TFSWJDFܳݽইم Entity - Entity Layer Repository - Domain Layer Interface -FHBDZ"SDIJUFDUVSF - ..
  14. -FHBDZ"SDIJUFDUVSF - .. 이 구조를 한장에 모아보면 이렇게 정리할 수

    있습니다. 보라색의 Presentation Layer는 오직 context의 service만 알게되는 형태이고 갈색의 services에서 각 서비스는 Repository들을 주입받습니다. context의 services안에 있는 Repositories는 파란색의 data영역의 Repository들을 모아둔 것입니다. Repository는 core에서 interface영역을 정의하고 data에 실제 구현체 부분이 있습니다 사실 이렇게만 설명드리면, 기존 구조가 어떠한 흐름이었는지 설명드리기에 충분하지 않을 것 같습니다.
  15. Context - 대운동장 Presentation services repositories View - Presentation Layer

    - router, component 등 WJFX TFSWJDF core Service - Domain Layer http - Data Layer data Repository - Data Layer EBUBSFQPDPSFSFQPܳ*NQMFNFOUT DPOUFYUTFSWJDFTחDPSF੄TFSWJDFܳݽইم Entity - Entity Layer Repository - Domain Layer Interface -FHBDZ"SDIJUFDUVSF - ..
  16. -FHBDZ"SDIJUFDUVSF - .. 하지만, 오늘 발표에서 중점적으로 다루고자 하는 내용은

    옛 아키텍처에 대한 flow가 아니므로 여기서 view부분과 repository부분만 살짝 보고 가려고 합니다.
  17. -FHBDZ"SDIJUFDUVSF - .. 먼저 View 부분입니다. 예시로 포켓몬 필드라는 컴포넌트를

    만들어 보았습니다.
  18. new Context를 통해 생성한 Application의 service호출 -FHBDZ"SDIJUFDUVSF - .. 아까

    보았던 Context를 new Context를 이용해서 Application 이라는 이름으로 생성했고, 이 구문을 통해 생성한 Application의 service를 호출합니다.
  19. service중 포켓몬 수집 서비스 참조 -FHBDZ"SDIJUFDUVSF - .. 그리고 많은

    서비스중 ‘수집 서비스’ 를 호출하고,
  20. 수집 서비스의 catch 함수 호출 -FHBDZ"SDIJUFDUVSF - .. 수집서비스가 수행하는

    동작중 하나인 catch를 호출합니다.
  21. interface로 정의된 Repository를 주입받는다. catch৬ call਷ ࢎਊ੗੄ use case۽ ࠅ

    ࣻ ੓णפ׮. ೙ਃೠ ૒੽੸ੋ ৻ࠗ ܻࣗझח Ӓ ޖ঺ب աఋա੓૑ ঋणפ׮. ੉ ࠗ࠙਷ ,߸ೡ ࣻ ੓ח ਃࣗ۽ࠗఠ ܻ࠙दఃӝ ਤೣੑפ׮. -FHBDZ"SDIJUFDUVSF - ..
  22. interface로 정의된 Repository를 주입받는다. catch৬ call਷ ࢎਊ੗੄ use case۽ ࠅ

    ࣻ ੓णפ׮. ೙ਃೠ ૒੽੸ੋ ৻ࠗ ܻࣗझח Ӓ ޖ঺ب աఋա੓૑ ঋणפ׮. ੉ ࠗ࠙਷ ,߸ೡ ࣻ ੓ח ਃࣗ۽ࠗఠ ܻ࠙दఃӝ ਤೣੑפ׮. -FHBDZ"SDIJUFDUVSF - .. view가 부르게 되는 각 서비스는 이렇게 interface형태로 된 Repository를 주입받습니다. 이 코드를 보면 Service가 주입받는 repo는 하단에 interface형태로 된, 실제 구현체가 아닙니다. catch와 call은 사용자의 useCase, 즉 행동으로 볼 수 있고 이 단계에서는 필요한 직접적인 외부 리소스가 명시되어 있지 않습니다.
  23. -FHBDZ"SDIJUFDUVSF - .. service에 주입해주었던 interface형태의 실제 구현체 부분입니다.

  24. -FHBDZ"SDIJUFDUVSF - .. 필요한 storage & api를 주입받는다. api와 storage는

    ‘외부 리소스’로 볼수 있고 이런 외부 리소스는 이 단계에서 주입받습니다.
  25. ਬ੷೯ز VTF$BTF ਷߸ೞ૑ঋҊ : Domain Layer 이렇게 하면 유저의 행동,

    즉 useCase인 catch와 call을 정의한 interface형태의 Repository는 변하지 않고,
  26. ৻ࠗ੄ઓࢿ݅߸҃оמ ਬ੷೯ز VTF$BTF ਷߸ೞ૑ঋҊ 외부 의존성에 해당하는 storgae나 api 설정만

    변경할 수 있습니다. 만약 storage나 api를 다른것을 사용하고 싶다면 interface영역은 수정하지 않고 data layer의 Repository생성자만 수정하면 됩니다.
  27. https://speakerdeck.com/soyoung210/clean-architecture-in-banksalad -FHBDZ"SDIJUFDUVSF - .. 사실 이 설명만으로는 기존 구조에 대해서

    충분히 말씀드리지 못했고, 아마 이해가 잘 안되실것 같다고도 생각됩니다. 하지만 오늘 중점적으로 다룰 부분은 이 구조에 대해서가 아니라, 이사 과정에 대한 이야기 입니다. 그럼에도 불구하고, 혹시 이 구조에 대해서 더 알고싶은 분이 계시다면 위 발표자료를 참고하시면 좋을것 같습니다.
  28. ੉ࢎܳ೧ঠѷ׮

  29. ੉ࢎܳ೧ঠѷ׮ ৵  언뜻 보면,테스트도 쉬울것 같고 괜찮은것 같은데 왜

    이사를 해야겠다고 결심했을까요?
  30. ੉ࢎܳѾबೞ؍ૉ਺ʜ 그 얘기를 하기전에, 우선, 이사를 결심하던 즈음 있었던 변화에

    대해 먼저 말씀드리겠습니다.
  31. ਢ ৘੸Ә୶ୌ ஠٘୶ୌ PFM ݒѢ૓ ؀୹୶ୌ … 8FC3FQPীח 기존에 웹

    레포에는 당연히 웹서비스에 대한 내용이 관리되고 있었습니다.
  32. ਢ ৘੸Ә୶ୌ ஠٘୶ୌ PFM ݒѢ૓ ؀୹୶ୌ … 8FC3FQPীח ؀୹ ࠁ೷

    ஠٘ ਢ࠭ … 하지만, 앱이 출시되면서 앱내 웹뷰를 위한 코드가 추가되기 시작했습니다. 처음에 이 웹뷰를 빠르고 효율적으로 개발하기 위해 하나의 레포에서 개발을 진행했습니다.
  33. 8FC3FQPীח + PC 처음 지었던 집은 PC서비스만을 고려해서 지은집이었는데,

  34. 8FC3FQPীח + PC 웹뷰 작업을 위한 코드들이 하나 둘 추가되면서

  35. 8FC3FQPীח PC PC + 웹이 살던 작은 집은 기능이 많이

    붙은 다소 크고 이상한 집이 되었습니다.
  36. 8FC3FQPীח ..
 (a.k.a useCase) useCase 많은 서비스를 관리하기 위해 코드량도

    정말 방대해졌고, 특히 사용자의 행동을 정의하는 useCase쪽 파일은 정말 크기가 어마어마해 졌습니다.
  37. build .gif 큰 몬스터 집이 된 웹 집은 빌드 돌렸을때는

    우선 컴퓨터가 이륙하려고 했고 가끔 메모리가 부족한 상황에서 돌리면 죽기도 했습니다.
  38. : Entity ..? 그리고 이렇게 많은 서비스를 관리하다 보니 피쳐를

    추가할때, 도메인 개념 단위인 Entity를 추가해야 할지 말아야할지도 헷갈렸습니다.
  39. : ☠ entity ? ..? Entity라는 폴더내에 이렇게 많은 것들이

    관리되고 있었기 때문입니다. entity의 홍수같은 느낌이죠..? 늘 새로 정의해야하나 아니면 이미 정의되었나 찾아보는 것도 매우 고통스러운 일이이었습니다..
  40. : web . develop master ? : .. Cherry Pick

    QA
  41. ( ) : web . develop master ? : ..

    Cherry Pick QA
  42. 이런 이유로 저희팀은 각 서비스별로 저장소를 분리하기로 했습니다. 분리 하고

    프로젝트를 셋업하는 과정에서 과연 기존 구조와 동일하게 유지할 것인가에 대한 고민을 했습니다. 저희팀의 결론은 ‘기존 구조를 유지하지 말자! 였습니다.
  43. ѐࢶ೧ঠೡѪ

  44. 1. core ৔৉੉ ف ࠗ࠙ਵ۽ ա׍ 정의부 구현부 ѐࢶ೧ঠೡѪ 첫번째는,Repository를

    Interface와 실제 구현부로 나누어 정의하는 것에 효용을 느끼지 못했습니다. 사실, 이 당시에는 TestCode 작성이 고도화 되어 있지 않아서 일수도 있지만, 이 때의 상황에서는 개발을 진행함에 있어 아무런 이득을 느끼지 못한다는 결론을 내렸습니다.
  45. ~✌ 정의부 구현부 ѐࢶ೧ঠೡѪ 1. core ৔৉੉ ف ࠗ࠙ਵ۽ ա׍

  46. 2. ୊਺ ٜ݅ٸח જওחؘ… : ੉ featureܳ ੉ઁ ײইঠ ѷ׮.

    ѐࢶ೧ঠೡѪ 두번째로 개선해야 했던 점은, 서비스를 부수는데 너무 어려웠다는 것입니다.
  47. ѐࢶ೧ঠೡѪ 2. ୊਺ ٜ݅ٸח જওחؘ… ,7*&8- 부수기 위해 파일을 제거하는

    작업을 진행했습니다. 일단 관련된 view 파일들 부터 깔끔하게 휴지통으로 보내봤습니다.
  48. core Service - Domain Layer Entity - Entity Layer Repository

    - Domain Layer ѐࢶ೧ঠೡѪ 2. ୊਺ ٜ݅ٸח જওחؘ… view를 휴지통으로 보내고 나니, 해당 view에서 어떤 Repository와 Entity를 사용하고 있는지 파악하는 것이 어려웟습니다.
  49. Repository
 Dependencies repo 기존 코드에서 관리하고 있던 ServiceDependencies 파일입니다. RepositoryDependencies로

    주입받은 내용중 각 서비스에 맞는 repo를 선택해서 service를 생성할 때 넣어주게 됩니다.
  50. Repo RepositoryDependencies , . 삭제할 서비스가 repo.service3을 참조하고 있었다면, 이

    repo.service3을 사용하고 있는 다른 서비스가 생각보다 정말 많았습니다.
  51. Repo RepositoryDependencies , . service ..? repo.service3이 삭제할 서비스에만 사용되는지,

    다른 서비스에서 얼마나 참조되고 있는지 파악하는것은 정말 어려운 작업이 되어버렸습니다..
  52. Repo RepositoryDependencies , . 거대한 repository를 삭제하는 것은 너무 두려워서

    결국 view쪽 코드와 entity 코드 일부만 삭제하는 것으로 결정했습니다.
  53. : ৬! ӝמ੉ ୶о غ঻যਃ. Entity core/Respository core/service data/http/mapper data/Repository

    view 2. ୊਺ ٜ݅ٸח જওחؘ… ѐࢶ೧ঠೡѪ 부수는 과정도 만만치 않지만, 수정하는 작업또한 어려웠습니다. 하나의 수정에 대해 건드려야 하는 파일들이 너무 많았기 때문이죠. 덤으로, view쪽 구조가 data와 얽혀있어서 수정하는 것은 늘 두려웠습니다.
  54. 3. View৬ Dataо ೠҔী ѐࢶ೧ঠೡѪ https://notefolio.net/virgintruth/69097 api response api fetchState

    localState view helper render VIEW 마지막으로, view에 render외에 너무 많은 정보가 관리되고 있었습니다. api response, fetchState 그리고 렌더링에 필요한 local state, 데이터를 가공하기 위한 helper function등이 제대로 분리되어 있지 않았습니다.
  55. 1. core৔৉੉ ف ࠗ࠙ਵ۽ ա׍ 2. ୊਺ ٜ݅ٸח જওחؘ… 3.

    View৬ Dataо ೠ Ҕী ѐࢶ೧ঠೡѪ 정리하자면, 기존 구조에서는 이렇게 3가지 불편함이 있었습니다. 이는 기존에 사용했던 구조의 문제점이라기 보다는, 그 사용에 있어서 면밀히 고려하지 못했기 때문이라고 생각합니다.
  56. ࢜૘ਵ۽੉ࢎ ୐ߣ૩੉ࢎ$.4 ੉۠ ੉ਬ۽, CMSܳ ٜ݅ ٸ ࢜۽਍ ҳઑܳ بੑ೧ࠁӝ۽

    ೮णפ׮. break changeо ߊࢤೠ ࠗ࠙੉ۄҊ ࢤпغয ‘୐ ߣ૩ ੉ࢎ’ ۄҊ ಴അ೮णפ׮.
  57. ੉ࢎೡٸח؊જ਷૘ਵ۽ Repo CMS 0. ࢲ࠺झ ܻ࠙ service 앞서 말씀드렸듯, 구조에

    대한 변경을 결정하기 전에 저희팀은 서비스별로 저장소를 분리하자 라는 결정을 했습니다. 이런 상황이다보니 구조를 개선하는 작업은 기존 레거시를 모두 고치는 것이 아니라 비교적 작은 단위인 하나의 서비스부터 적용하는것이 가능했습니다.
  58. ੉ࢎೡٸח؊જ਷૘ਵ۽ 1. ੿੄৬ ҳഅਸ э੉ core 아까 말씀드린 첫 번째

    불편함은 core의 Repository영역이 정의부와 구현부로 나뉘어져있다는 것입니다.
  59. ੉ࢎೡٸח؊જ਷૘ਵ۽ Dependencies repo 1. ੿੄৬ ҳഅਸ э੉

  60. ੉ࢎೡٸח؊જ਷૘ਵ۽ Dependencies repo 1. ੿੄৬ ҳഅਸ э੉ 이를 하나로 합쳐,

    api provider라는 의존성을 바로 주입받고 RepositoryDependencies라고 하는, 모든 Repository를 모아두는 쪽에 바로 주입하는 형태로 작성했습니다. 코드량이 반으로 줄어드니까, 개발 단계에서 행복감은 두배로 상승했습니다.
  61. ੉ࢎೡٸח؊જ਷૘ਵ۽ 2. ࣻ੿ী ਊ੉ೞب۾ Entity data/Respository presentation/Container store(redux) Entity core/Respository

    core/service data/http/mapper data/Repository view ( ) Before After
  62. ੉ࢎೡٸח؊જ਷૘ਵ۽ 2. ࣻ੿ী ਊ੉ೞب۾ Entity data/Respository presentation/Container store(redux) Entity core/Respository

    core/service data/http/mapper data/Repository view Before After 두번째 개선은 첫번째 내용과 겹치는 부분도 있습니다. 수정사항이 생겼을 때 변경해야하는 파일의 양이 너무 많았었습니다. 구조를 간단하게하고, 역할을 명확히 함으로서 수정이 두렵지 않도록 했습니다.
  63. ੉ࢎೡٸח؊જ਷૘ਵ۽ 3. View৬ Data ܻ࠙ redux + redux observable view

    Data view와 Data를 분리하기 위한 목적으로, redux와 redux middleware를 도입하였습니다.
  64. झషয(Data) ޷ٜਝয(async api) dispatchח ೠҔ! renderী ૘઺! ੉ࢎೡٸח؊જ਷૘ਵ۽ 3. View৬

    Data ܻ࠙
  65. ੉ࢎೡٸח؊જ਷૘ਵ۽ 3. View৬ Data ܻ࠙ store는 server에서 받아오는 데이터를 명시하는

    역할로 사용했습니다. api호출과 관련된 처리는 미들웨어에 모두 위임하였습니다. view에서는 직접 api 호출과 관련된 로직을 관리하지 않고 오직 dispatch와 Store의 state만 바라보면 되는 구조로 변경하였습니다. Dispatch를 담당하는 곳을 한 레벨로 지정하는것까지 적용하여 각 요소의 역할을 나누었습니다.
  66. ੉੿بݶҡଳ਷ѐࢶ 1. ੿੄৬ ҳഅਸ э੉ 2. ࣻ੿ী ਊ੉ೞب۾ 3. View৬

    Data ܻ࠙ core ( ) view Data 이 프로젝트 중반정도까지, 이정도면 기존 구조에 비해 생산성도 높고 편하다고 생각했습니다.
  67. ޖ঺ਸ֬ଢ଼ਸө Store , dispatch ? mapStateToProps . mapDispatchToProps Route View

    . dispatch : dispatch ! ( : entry/view , : entry/container) ( hook react-redux 6.x) 중반까지라고 말씀드린 이유는, 끝날쯤에는 놓친부분들이 많이 보였기 때문입니다. 당시에는 react-redux 6버전을 사용하고 있었는데요, 해당 버전을 쓰신 분들이라면 아실 mapStateToProps와 mapDispatchToProps 즉 store와 connect되는 부분을 어디로 잡을지에 대한 고민이 있었습니다. 그리고, 이 고민을 썩 잘 해결하지 못했습니다. dispatch는 상태를 변화시킬 수 있으니까 가급적 한 곳에서만 이루어져야 한다고 생각했고 이 위치가 route의 바로 하위 레벨인 entry의 view폴더에서 이루어져야 한다고 생각했습니다.
  68. Root entry
 /view /user Container Container Container Container /search /login

    dispatch ! entry
 /view entry
 /view 그림으로 보면 이런 구조인데요, Route를 모두 모아두는 rootRoute하위 레벨인 entry에서 dispatch가 이루어지도록 했습니다.
  69. ޖ঺ਸ֬ଢ଼ਸө Store , dispatch ? ( hook react-redux 6.x) mapStateToProps

    . mapDispatchToProps Route View . dispatch Drilling ⚓, Component : dispatch ! ( : entry/view , : entry/container) ❓ 이렇게 생각했던 것은 끊임없는 Driling과 재사용 불가능한 Component를 만들었습니다. redux를 사용했던 목적이 Driliing해결은 아니었지만 실제 필요한 컴포넌트에게 전달되기까지 몇단계를 거치는 구조는 다소 고통스러웠습니다. 그리고 dispatch의 기준이 기본은 entry이고 필요하면 container라는 러프한 룰을 적용하다보니 더이상 기준이 아니게 되었고, props가 강제된 컴포넌트는 재사용이 불가능해졌습니다.
  70. ޖ঺ਸ֬ଢ଼ਸө action, reducer, middleware .. actions.ts epic.ts reducer.ts

  71. ޖ঺ਸ֬ଢ଼ਸө action, reducer, middleware .. 그리고 이건 주위에서 ‘redux를 사용하지

    않는 이유’ 중 하나로 많이 들었던 이야기 인데요, redux를 사용하기 위해 반복적으로 적어줘야 하는 코드가 많았습니다. 복사-붙여넣기 하면 손쉽게 작성할 수는 있지만, 중복코드가 늘어나는 작업은 꽤 귀찮은 일이었습니다.
  72. ޖ঺ਸ֬ଢ଼ਸө Data ? Project view data util api helper Project

    Data View .
  73. ޖ঺ਸ֬ଢ଼ਸө Data ? 마지막으로 놓친점은, 제가 너무 Data만 신경썼다는 점입니다.

    이전 구조에서 view와 data가 너무 심하게 바인딩 되어 있었다는 점에 포커스를 맞추다 보니 다른 부분들을 많이 놓쳤습니다. 하나의 프로젝트는 data와 view만으로 구성되지 않습니다. view, data외에도 api, util, view를 위한 helper function등이 프로젝트의 구성요소가 될 수 있습니다.
  74. ޖ঺ਸ֬ଢ଼ਸө Data ? Project view data util api helper helper

    ? view-model ..? controller ? ?
  75. ޖ঺ਸ֬ଢ଼ਸө Data ? 놓쳤던 문제를 조금더 자세히 설명드리자면 view에 필요한

    helper의 위치를 어디로 둘것인지 정하지 않았습니다. helper라고 해서 뭔가 좀 작아보이긴 하지만 사실 일종의 ‘로직처리’로 볼 수 있는 부분인데, 이를 명확히 정하지 않아서 로직이 곳곳에 흩뿌려진 상태가 되었습니다.
  76. ޖ঺ਸ֬ଢ଼ਸө Data ? Project view data util api helper util

    ? view Data util ?
  77. ޖ঺ਸ֬ଢ଼ਸө Data ? 그리고, helper와 유틸의 차이에 대한 기준을 명확히

    하지 않았습니다. 명확한 목적을 구분하지 않아 비슷한 역할을 하는 함수가 util에 있거나, helper function이 정의된 곳에 있거나.. 이렇게 케이스가 나뉘면서 다른사람이 보았을때 혹은 미래의 내가 보았을때 코드가 쉽게 파악되지 않는 문제가 있었습니다.
  78. ೠӒܶ੄झ౵ѱ౭௏٘

  79. ੉۞ೠ੉ਬ۽ CMS : CMS ? 4 : , . ?

    몇가지 아쉬웠던 점이 있지만 기존에 사용하던 구조에서 break change를 이뤄냈다는 것과 가설을 실제 작은 서비스 레벨로 구현하여 검증했다는 점에 우선 의미를 두기로 했습니다.
  80. ੉۞ೠ੉ਬ۽ ( ! ) 하지만 역시 다 만들고 나서야 아까같은

    스파게티 상태가 눈에밟혔습니다. 이를 개선하기 위해 다 부수고 다시 만드는 것은 일정상 불가능한 일이었으므로 이 프로젝트는 이대로 유지보수 하는것으로 하고, 묻고 다음 프로젝트에서 더블로 잘해보기로 했습니다.
  81. /PX જ਷૘ৈ੿੄അ੤ ӝઓ ҳઑܳ ѐࢶೞ੗ח ೠߣ੄ ௾ Ѿबਸ ೮૑݅ ҅ࣘ೧ࢲ

    ѐࢶ੉ ೙ਃೞ׮ח Ѫਸ ݆੉ וԕणפ׮. જ਷ ૘ ৈ੿਷ അ੤ ૓೯઺ੑפ׮.
  82. 프로젝트개선의 현재에 대한 이야기를하기전에, 여러분은 만약 이사를 고려하신다면 어떤 점들을

    고려하시나요? 저는 얼마전에 이사를 했는데 단순히 집의 모양새 뿐만 아니라 주변 교통이나 상권등도 이사에 있어 중요한 요소였습니다.
  83. ? 이는 프로젝트 구성시 고려하는 것과 별반 다르지 않습니다.

  84. ೐۽ં౟ܳ ҳࢿೞח ਃࣗܳ ଘ աৌ೧ࠁҊ, ݾ੸ী ݏѱ ੤ߓৌ ೞӝ. 그래서

    두번째 프로젝트를 더블로 잘하기 위해 이렇게 칠판에 프로젝트의 전체 그림을 그렸었습니다.
  85. 이렇게 칠판에 열심히 적으면서 잡아본 현재구조는 이런 형태입니다. api, asset,

    domain, presentation, store와 util 폴더로 구성되어 있습니다.
  86. index.ts api의 index에는 header, requestGet같은 공통적으로 사용할 함수를 만들어둡니다.

  87. .ts api폴더 하위의 각 파일에서는 공통적으로 만들어 둔 함수를 이용해서

    실제 api를 호출하는 함수를 만들어둡니다.
  88. components / container / routes / view presentation쪽은 components, container,

    routes, view 크게 4개로 구성됩니다.
  89. components / container / routes / view domain 먼저 component는

    domain을 알지못하는, 항상 재사용을 고려해서 작성되어야 하는 부분입니다.
  90. components / container / routes / view domain Primitive Type

    Props . interface Props { name: string; value: string; } component는 무조건 primitive 타입의 props만 가집니다. 개발자가 따로 정의한 entity를 props로 가질 수 없는 부분입니다. entity를 가지게 되면 도메인을 알게되고, 이렇게 되면 다른곳에서 재사용을 하지 못할 확률이 높아지기 때문입니다.
  91. components / container / routes / view domain Props .

    , container는 domain props를 가질수도 있는 부분으로 정의하였습니다. 이 부분은 재사용을 할수도, 하지 않을 수도 있습니다. 재사용성은 컨테이너에게 주요한 관심사가 아닙니다.
  92. components / container / routes / view domain Props .

    , Entity Type Props . interface Props { poke: Pokemon; count: number; } 예시를 들자면, 이렇게 개발자가 정의한 custom entity인 Pokemon을 props로 가질 수 있습니다. 여기 예시 코드를 보면 custom entity인 Pokemon과 primitive type인 count를 props로 가지고 있습니다.
  93. components / container / routes / view domain Props .

    , Entity Type Props . , dispatch + ) tmi: react-redux 7.1 도메인을 알게되는 부분이기 때문에 자연스럽게 dispatch는 컨테이너 레벨에서 이루어지게 됩니다. 그리고, eact-redux 7.1 버전의 useSelector와 useDispatch를 사용하게 되면서 코드량이 확 줄었습니다. connect와 mapdispatchToProps등을 정의할 필요가 없어져서 훨씬 간결하게 사용할 수 있으니 이 문법을 사용해보시는 것을 추천드립니다.
  94. components / container / routes / view Route : component

    view View : container route쪽에서 routing을 담당하고, 여기에 매칭되는 component가 view입니다. view에서는 비즈니스 로직없이 layout만 담당하게 됩니다.
  95. "OE , Project view data util api helper 아까 소개되었던

    프로젝트의 5가지 구성요소 입니다.
  96. Project view data util api helper "OE , : helper

    controller ! 1 (util ) view(container || component)
  97. "OE , 모호했던 helper function에 대해서는 이렇게 정리했습니다. helper의 위치는

    component혹은 container 하위의 controller라는 폴더에 위치해야 한다는 규 칙을 세웠습니다. util과는 다르게 view를 위한 함수이며 1회성에 가까운 역할을 한다고 정의했습니다.
  98. Project view data util api helper "OE , : util

    , view !
  99. "OE , 반대로 util은 특정 component나 container가 아니라 어디서든 범용적으로

    사용가능해야 하며, 자연히 추상화 레벨이 높아야 합니다. 너무 많은 결정을 하는 함수는 util에 두지 않기로 했습니다.
  100. ࢜۽਍૘ : ( ) , ~ ! : ( )

    그리고, 새로운 집에 리덕스를 도입하고 공유하는 과정에서 팀원들이 새집증후군을 느꼈는데요,
  101. ࢜۽਍૘ : ! : 이를 해소할 무언가가 필요했습니다. 여기서부턴 아까

    말씀드렸던 리덕스를 쓰면서 불편했던점에 대한 꿀팁정도로 이해해주시면 좋을것 같습니다.
  102. ࢜૘ૐറҵృ஖ܻ؋झಞ action, reducer, middleware . redux . . 아까 액션,

    리듀서, 미들웨어를 정의하는데있어 비슷한 코드를 매번 적어주어야 한다는 불편함이 있었는데요, 생각해보니 redux는 잘못이 없고 제가 조금 잘못사용하고 있지 않나.. 라는 생각이 들었습니다. 오른쪽 사진이 리덕스 새집증후군을 해결하기 위한 actionUtil 함수인데요,
  103. redux . . const action = createAsyncAction<number>(PREFIX) action, reducer, middleware

    . const epic = createAsyncEpic(PREFIX, apiCallFunction) ࢜૘ૐറҵృ஖ܻ؋झಞ
  104. 이를 자세히 보자면 이런 구조입니다. createAsynAction이라는 유틸성 함수를 정의하고 이

    함수는 3개의 프리픽스와 액션 함수를 리턴 합니다. 사용하는 쪽에서는 함수 하나만 호출하면 바로 액션정의가 끝나게 됩니다. 미들웨어 또한 이런식으로 작성해서 중복코드를 줄였습니다.
  105. reducer switch - case action, reducer, middleware . ࢜૘ૐറҵృ஖ܻ؋झಞ 리듀서

    쪽에서도 기존의 switch-case 문법을 걷어냈습니다. 여기 보시는건 로딩 리듀서인데요, fetchState를 관리하는 것은 모든 리듀서에서 필요했던 요소여서 로딩 리듀서 또한 유틸성으로 분리했습니다.
  106. : redux actionUtil ! 
 ! 이렇게 액션유틸을 만들고 팀원들에게

    공유해본결과,
  107. 초기셋팅 비용이 많이 들고 중복코드가 많아서 괴로웠던 점이 어느정도 해소

    되었다는 답을 들을 수 있었습니다.
  108. ( ) 이런 점들을 고쳐나가며 프로젝트를 만들어나가고 있는데, 아직까지는 괜찮은

    구조인것 같습니다. 각각이 어디에 있는지 명확하며 리뷰하기에 쉬운 코드를 만들기 위해 열심히 노력하고 있습니다.
  109. /ߣ੄੉ࢎҗ੿ਸѻਵݴʜ ੉ࢎܳೞח੉ਬ ੉ۧѱ ௼ѱח ೠߣ, ੘ѱೠߣ / ୨ فߣ੄ ҳઑ

    ѐࢶҗ੿ਸ ࣗѐ೧٘۷חؘਃ, ઁо ҳઑ Ҋ஖ӝо ஂ޷о ইפۄݶ ৵ ੗Բ ੉ࢎܳ ೞחѤ૑ ੄ޙ੼ਸ о૑न ٜ࠙ب ҅पѪ эणפ׮.
  110. #FTU1SBDUJDFۆ 바로 BestPractice를 찾기 위해서 입니다. 그런데 Best Practice란 무엇일까요?

  111. #FTU1SBDUJDFۆ ❌ 저는 아직 못찾았고, 사실 그런건 없는게 아닌가.. 라는

    생각도 듭니다.
  112. #FTU1SBDUJDFۆ 왜냐하면, ‘좋다’ 라는 정의는 팀의 상황과 서비스에 따라 달라질

    수 있기 때문입니다. 그래서, 좋은 패턴을 위해서는, 팀 구성원이 머리를 맞대고 합의해나가는 과정이 필요합니다. 문제점을 의식하고 공유하여 우리팀에 맞는 더 나은 구조를 계속 찾아가보고 있습니다.
  113. : ( ) : ( ) ! 그리고, 저장소가 분리되고

    매번 구조를 Set up하는 것은 비용이 많이들고 힘든 일이 될 수 있습니다. 이를 개선하기 위해 Project Scaffolding 도구를 만들었습니다.
  114. sample project template repository를 만들고, 각 폴더에 변경된 구조를 아카이빙

    하기로 했습니다. github api를 활용해서 api call까지 하는 형태의 샘플 프로젝트를 만들어서 아카이빙 하고 있습니다.
  115. . sample project sca olding 이렇게 샘플프로젝트를 만들면 새로운 구조에

    대해서 누구나 쉽고 간단하게 검증해볼 수 있습니다. 저희 팀에서 관리하는 대부분의 프로젝트는 api response를 이용하여 이를 가공하는 형태의 프로젝트 이기 때문이죠. 덤으로, 스캐폴딩도구도 만들 수있으니 서비스별로 저장소를 분리한 저희팀에 유용한 방식이 아닌가.. 하고 생각하고 있습니다.
  116. ( ) 이런저런 과정을 거쳐서 아직 구조는 조정중에 있습니다. 지금

    사용하고 있는 구조역시, 결국 불편한 점이 있다면 다른 구조를 선택할 수 있습니다. 더 많은 검증이 필요하고 계속해서 불편한점에 귀기울이며 프로젝트는 계속 구조조정 중입니다.
  117. хࢎ೤פ׮ ੉ࣗ৔]TPZPVOH!SBJOJTUDPN]IUUQTTPTPEFW