Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

: , , . : . , . , . : FEConf( ) . , speakerdeck . ೴૘઴ѱ ࢜૘׮য় 스크립트 영역 스크립트 영역

Slide 3

Slide 3 text

੉ࢎܳ ೧ঠѷ׮! Nߣ੄ ੉ࢎҗ੿ਸ ѻਵݴ… য়ט׮ܙ੉ঠӝ જ਷ ૘੉঻؍, Legacy Architecture

Slide 4

Slide 4 text

-FHBDZ"SDIJUFDUVSF જ਷૘੉঻؍

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

클라이언트는 제품의 정보가 수렴하는 곳. 프로젝트는 관리하기 어려워집니다. 이 모든 카테고리가 수렴하는 곳이 클라이언트, 즉 웹프론트엔드 입니다. 이렇게 여러가지 서비스를 제공하는 하나의 프로젝트를 어떻게 구성할 것인가에 대한 저희팀의 첫번째 답은,

Slide 8

Slide 8 text

%PNBJO ઺बਵ۽ࣗ೐౟ਝযܳࢸ҅ೞחѪ %PNBJO 그러나 DDD는 아닌..

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

-FHBDZ"SDIJUFDUVSF - .. 1SFTFOUBUJPO %BUB %PNBJO &OUJUZ Layer Layer use case Layer Layer -BZFSܻ࠙৬੄ઓࢿ઱ੑী'PDVT 전체 구조를 이렇게 4가지 레이어로 분리했습니다. 이 구조는 Layer의 분리와 의존성주입에 Focus를 맞추어 설계했습니다.

Slide 11

Slide 11 text

-FHBDZ"SDIJUFDUVSF - .. 얼마전 발표했던 CleanArchitecture in Banksalad의 마지막 장표인데요, 그 때 이 구조를 소개할 때 장점으로 ‘View는 Service만 알면 되고, 그 행동은 주입받은 리소스에 의존하기 때문에 이 규칙만 이해하면 빠르고 규칙적인 개발이 가능하다고 말씀드렸었습니다.

Slide 12

Slide 12 text

-FHBDZ"SDIJUFDUVSF - .. 기존 구조는 이렇게 Context에서 apiProvider, service 등 프로젝트에 필요한 요소를 모두 모아서 하나의 인스턴스로 생성하는 형태였습니다.

Slide 13

Slide 13 text

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 - ..

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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 - ..

Slide 16

Slide 16 text

-FHBDZ"SDIJUFDUVSF - .. 하지만, 오늘 발표에서 중점적으로 다루고자 하는 내용은 옛 아키텍처에 대한 flow가 아니므로 여기서 view부분과 repository부분만 살짝 보고 가려고 합니다.

Slide 17

Slide 17 text

-FHBDZ"SDIJUFDUVSF - .. 먼저 View 부분입니다. 예시로 포켓몬 필드라는 컴포넌트를 만들어 보았습니다.

Slide 18

Slide 18 text

new Context를 통해 생성한 Application의 service호출 -FHBDZ"SDIJUFDUVSF - .. 아까 보았던 Context를 new Context를 이용해서 Application 이라는 이름으로 생성했고, 이 구문을 통해 생성한 Application의 service를 호출합니다.

Slide 19

Slide 19 text

service중 포켓몬 수집 서비스 참조 -FHBDZ"SDIJUFDUVSF - .. 그리고 많은 서비스중 ‘수집 서비스’ 를 호출하고,

Slide 20

Slide 20 text

수집 서비스의 catch 함수 호출 -FHBDZ"SDIJUFDUVSF - .. 수집서비스가 수행하는 동작중 하나인 catch를 호출합니다.

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

-FHBDZ"SDIJUFDUVSF - .. service에 주입해주었던 interface형태의 실제 구현체 부분입니다.

Slide 24

Slide 24 text

-FHBDZ"SDIJUFDUVSF - .. 필요한 storage & api를 주입받는다. api와 storage는 ‘외부 리소스’로 볼수 있고 이런 외부 리소스는 이 단계에서 주입받습니다.

Slide 25

Slide 25 text

ਬ੷೯ز VTF$BTF ਷߸ೞ૑ঋҊ : Domain Layer 이렇게 하면 유저의 행동, 즉 useCase인 catch와 call을 정의한 interface형태의 Repository는 변하지 않고,

Slide 26

Slide 26 text

৻ࠗ੄ઓࢿ݅߸҃оמ ਬ੷೯ز VTF$BTF ਷߸ೞ૑ঋҊ 외부 의존성에 해당하는 storgae나 api 설정만 변경할 수 있습니다. 만약 storage나 api를 다른것을 사용하고 싶다면 interface영역은 수정하지 않고 data layer의 Repository생성자만 수정하면 됩니다.

Slide 27

Slide 27 text

https://speakerdeck.com/soyoung210/clean-architecture-in-banksalad -FHBDZ"SDIJUFDUVSF - .. 사실 이 설명만으로는 기존 구조에 대해서 충분히 말씀드리지 못했고, 아마 이해가 잘 안되실것 같다고도 생각됩니다. 하지만 오늘 중점적으로 다룰 부분은 이 구조에 대해서가 아니라, 이사 과정에 대한 이야기 입니다. 그럼에도 불구하고, 혹시 이 구조에 대해서 더 알고싶은 분이 계시다면 위 발표자료를 참고하시면 좋을것 같습니다.

Slide 28

Slide 28 text

੉ࢎܳ೧ঠѷ׮

Slide 29

Slide 29 text

੉ࢎܳ೧ঠѷ׮ ৵ 언뜻 보면,테스트도 쉬울것 같고 괜찮은것 같은데 왜 이사를 해야겠다고 결심했을까요?

Slide 30

Slide 30 text

੉ࢎܳѾबೞ؍ૉ਺ʜ 그 얘기를 하기전에, 우선, 이사를 결심하던 즈음 있었던 변화에 대해 먼저 말씀드리겠습니다.

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

ਢ ৘੸Ә୶ୌ ஠٘୶ୌ PFM ݒѢ૓ ؀୹୶ୌ … 8FC3FQPীח ؀୹ ࠁ೷ ஠٘ ਢ࠭ … 하지만, 앱이 출시되면서 앱내 웹뷰를 위한 코드가 추가되기 시작했습니다. 처음에 이 웹뷰를 빠르고 효율적으로 개발하기 위해 하나의 레포에서 개발을 진행했습니다.

Slide 33

Slide 33 text

8FC3FQPীח + PC 처음 지었던 집은 PC서비스만을 고려해서 지은집이었는데,

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

8FC3FQPীח PC PC + 웹이 살던 작은 집은 기능이 많이 붙은 다소 크고 이상한 집이 되었습니다.

Slide 36

Slide 36 text

8FC3FQPীח ..
 (a.k.a useCase) useCase 많은 서비스를 관리하기 위해 코드량도 정말 방대해졌고, 특히 사용자의 행동을 정의하는 useCase쪽 파일은 정말 크기가 어마어마해 졌습니다.

Slide 37

Slide 37 text

build .gif 큰 몬스터 집이 된 웹 집은 빌드 돌렸을때는 우선 컴퓨터가 이륙하려고 했고 가끔 메모리가 부족한 상황에서 돌리면 죽기도 했습니다.

Slide 38

Slide 38 text

: Entity ..? 그리고 이렇게 많은 서비스를 관리하다 보니 피쳐를 추가할때, 도메인 개념 단위인 Entity를 추가해야 할지 말아야할지도 헷갈렸습니다.

Slide 39

Slide 39 text

: ☠ entity ? ..? Entity라는 폴더내에 이렇게 많은 것들이 관리되고 있었기 때문입니다. entity의 홍수같은 느낌이죠..? 늘 새로 정의해야하나 아니면 이미 정의되었나 찾아보는 것도 매우 고통스러운 일이이었습니다..

Slide 40

Slide 40 text

: web . develop master ? : .. Cherry Pick QA

Slide 41

Slide 41 text

( ) : web . develop master ? : .. Cherry Pick QA

Slide 42

Slide 42 text

이런 이유로 저희팀은 각 서비스별로 저장소를 분리하기로 했습니다. 분리 하고 프로젝트를 셋업하는 과정에서 과연 기존 구조와 동일하게 유지할 것인가에 대한 고민을 했습니다. 저희팀의 결론은 ‘기존 구조를 유지하지 말자! 였습니다.

Slide 43

Slide 43 text

ѐࢶ೧ঠೡѪ

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

~✌ 정의부 구현부 ѐࢶ೧ঠೡѪ 1. core ৔৉੉ ف ࠗ࠙ਵ۽ ա׍

Slide 46

Slide 46 text

2. ୊਺ ٜ݅ٸח જওחؘ… : ੉ featureܳ ੉ઁ ײইঠ ѷ׮. ѐࢶ೧ঠೡѪ 두번째로 개선해야 했던 점은, 서비스를 부수는데 너무 어려웠다는 것입니다.

Slide 47

Slide 47 text

ѐࢶ೧ঠೡѪ 2. ୊਺ ٜ݅ٸח જওחؘ… ,7*&8- 부수기 위해 파일을 제거하는 작업을 진행했습니다. 일단 관련된 view 파일들 부터 깔끔하게 휴지통으로 보내봤습니다.

Slide 48

Slide 48 text

core Service - Domain Layer Entity - Entity Layer Repository - Domain Layer ѐࢶ೧ঠೡѪ 2. ୊਺ ٜ݅ٸח જওחؘ… view를 휴지통으로 보내고 나니, 해당 view에서 어떤 Repository와 Entity를 사용하고 있는지 파악하는 것이 어려웟습니다.

Slide 49

Slide 49 text

Repository
 Dependencies repo 기존 코드에서 관리하고 있던 ServiceDependencies 파일입니다. RepositoryDependencies로 주입받은 내용중 각 서비스에 맞는 repo를 선택해서 service를 생성할 때 넣어주게 됩니다.

Slide 50

Slide 50 text

Repo RepositoryDependencies , . 삭제할 서비스가 repo.service3을 참조하고 있었다면, 이 repo.service3을 사용하고 있는 다른 서비스가 생각보다 정말 많았습니다.

Slide 51

Slide 51 text

Repo RepositoryDependencies , . service ..? repo.service3이 삭제할 서비스에만 사용되는지, 다른 서비스에서 얼마나 참조되고 있는지 파악하는것은 정말 어려운 작업이 되어버렸습니다..

Slide 52

Slide 52 text

Repo RepositoryDependencies , . 거대한 repository를 삭제하는 것은 너무 두려워서 결국 view쪽 코드와 entity 코드 일부만 삭제하는 것으로 결정했습니다.

Slide 53

Slide 53 text

: ৬! ӝמ੉ ୶о غ঻যਃ. Entity core/Respository core/service data/http/mapper data/Repository view 2. ୊਺ ٜ݅ٸח જওחؘ… ѐࢶ೧ঠೡѪ 부수는 과정도 만만치 않지만, 수정하는 작업또한 어려웠습니다. 하나의 수정에 대해 건드려야 하는 파일들이 너무 많았기 때문이죠. 덤으로, view쪽 구조가 data와 얽혀있어서 수정하는 것은 늘 두려웠습니다.

Slide 54

Slide 54 text

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등이 제대로 분리되어 있지 않았습니다.

Slide 55

Slide 55 text

1. core৔৉੉ ف ࠗ࠙ਵ۽ ա׍ 2. ୊਺ ٜ݅ٸח જওחؘ… 3. View৬ Dataо ೠ Ҕী ѐࢶ೧ঠೡѪ 정리하자면, 기존 구조에서는 이렇게 3가지 불편함이 있었습니다. 이는 기존에 사용했던 구조의 문제점이라기 보다는, 그 사용에 있어서 면밀히 고려하지 못했기 때문이라고 생각합니다.

Slide 56

Slide 56 text

࢜૘ਵ۽੉ࢎ ୐ߣ૩੉ࢎ$.4 ੉۠ ੉ਬ۽, CMSܳ ٜ݅ ٸ ࢜۽਍ ҳઑܳ بੑ೧ࠁӝ۽ ೮णפ׮. break changeо ߊࢤೠ ࠗ࠙੉ۄҊ ࢤпغয ‘୐ ߣ૩ ੉ࢎ’ ۄҊ ಴അ೮णפ׮.

Slide 57

Slide 57 text

੉ࢎೡٸח؊જ਷૘ਵ۽ Repo CMS 0. ࢲ࠺झ ܻ࠙ service 앞서 말씀드렸듯, 구조에 대한 변경을 결정하기 전에 저희팀은 서비스별로 저장소를 분리하자 라는 결정을 했습니다. 이런 상황이다보니 구조를 개선하는 작업은 기존 레거시를 모두 고치는 것이 아니라 비교적 작은 단위인 하나의 서비스부터 적용하는것이 가능했습니다.

Slide 58

Slide 58 text

੉ࢎೡٸח؊જ਷૘ਵ۽ 1. ੿੄৬ ҳഅਸ э੉ core 아까 말씀드린 첫 번째 불편함은 core의 Repository영역이 정의부와 구현부로 나뉘어져있다는 것입니다.

Slide 59

Slide 59 text

੉ࢎೡٸח؊જ਷૘ਵ۽ Dependencies repo 1. ੿੄৬ ҳഅਸ э੉

Slide 60

Slide 60 text

੉ࢎೡٸח؊જ਷૘ਵ۽ Dependencies repo 1. ੿੄৬ ҳഅਸ э੉ 이를 하나로 합쳐, api provider라는 의존성을 바로 주입받고 RepositoryDependencies라고 하는, 모든 Repository를 모아두는 쪽에 바로 주입하는 형태로 작성했습니다. 코드량이 반으로 줄어드니까, 개발 단계에서 행복감은 두배로 상승했습니다.

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

੉ࢎೡٸח؊જ਷૘ਵ۽ 2. ࣻ੿ী ਊ੉ೞب۾ Entity data/Respository presentation/Container store(redux) Entity core/Respository core/service data/http/mapper data/Repository view Before After 두번째 개선은 첫번째 내용과 겹치는 부분도 있습니다. 수정사항이 생겼을 때 변경해야하는 파일의 양이 너무 많았었습니다. 구조를 간단하게하고, 역할을 명확히 함으로서 수정이 두렵지 않도록 했습니다.

Slide 63

Slide 63 text

੉ࢎೡٸח؊જ਷૘ਵ۽ 3. View৬ Data ܻ࠙ redux + redux observable view Data view와 Data를 분리하기 위한 목적으로, redux와 redux middleware를 도입하였습니다.

Slide 64

Slide 64 text

झషয(Data) ޷ٜਝয(async api) dispatchח ೠҔ! renderী ૘઺! ੉ࢎೡٸח؊જ਷૘ਵ۽ 3. View৬ Data ܻ࠙

Slide 65

Slide 65 text

੉ࢎೡٸח؊જ਷૘ਵ۽ 3. View৬ Data ܻ࠙ store는 server에서 받아오는 데이터를 명시하는 역할로 사용했습니다. api호출과 관련된 처리는 미들웨어에 모두 위임하였습니다. view에서는 직접 api 호출과 관련된 로직을 관리하지 않고 오직 dispatch와 Store의 state만 바라보면 되는 구조로 변경하였습니다. Dispatch를 담당하는 곳을 한 레벨로 지정하는것까지 적용하여 각 요소의 역할을 나누었습니다.

Slide 66

Slide 66 text

੉੿بݶҡଳ਷ѐࢶ 1. ੿੄৬ ҳഅਸ э੉ 2. ࣻ੿ী ਊ੉ೞب۾ 3. View৬ Data ܻ࠙ core ( ) view Data 이 프로젝트 중반정도까지, 이정도면 기존 구조에 비해 생산성도 높고 편하다고 생각했습니다.

Slide 67

Slide 67 text

ޖ঺ਸ֬ଢ଼ਸө 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폴더에서 이루어져야 한다고 생각했습니다.

Slide 68

Slide 68 text

Root entry
 /view /user Container Container Container Container /search /login dispatch ! entry
 /view entry
 /view 그림으로 보면 이런 구조인데요, Route를 모두 모아두는 rootRoute하위 레벨인 entry에서 dispatch가 이루어지도록 했습니다.

Slide 69

Slide 69 text

ޖ঺ਸ֬ଢ଼ਸө 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가 강제된 컴포넌트는 재사용이 불가능해졌습니다.

Slide 70

Slide 70 text

ޖ঺ਸ֬ଢ଼ਸө action, reducer, middleware .. actions.ts epic.ts reducer.ts

Slide 71

Slide 71 text

ޖ঺ਸ֬ଢ଼ਸө action, reducer, middleware .. 그리고 이건 주위에서 ‘redux를 사용하지 않는 이유’ 중 하나로 많이 들었던 이야기 인데요, redux를 사용하기 위해 반복적으로 적어줘야 하는 코드가 많았습니다. 복사-붙여넣기 하면 손쉽게 작성할 수는 있지만, 중복코드가 늘어나는 작업은 꽤 귀찮은 일이었습니다.

Slide 72

Slide 72 text

ޖ঺ਸ֬ଢ଼ਸө Data ? Project view data util api helper Project Data View .

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

ޖ঺ਸ֬ଢ଼ਸө Data ? Project view data util api helper helper ? view-model ..? controller ? ?

Slide 75

Slide 75 text

ޖ঺ਸ֬ଢ଼ਸө Data ? 놓쳤던 문제를 조금더 자세히 설명드리자면 view에 필요한 helper의 위치를 어디로 둘것인지 정하지 않았습니다. helper라고 해서 뭔가 좀 작아보이긴 하지만 사실 일종의 ‘로직처리’로 볼 수 있는 부분인데, 이를 명확히 정하지 않아서 로직이 곳곳에 흩뿌려진 상태가 되었습니다.

Slide 76

Slide 76 text

ޖ঺ਸ֬ଢ଼ਸө Data ? Project view data util api helper util ? view Data util ?

Slide 77

Slide 77 text

ޖ঺ਸ֬ଢ଼ਸө Data ? 그리고, helper와 유틸의 차이에 대한 기준을 명확히 하지 않았습니다. 명확한 목적을 구분하지 않아 비슷한 역할을 하는 함수가 util에 있거나, helper function이 정의된 곳에 있거나.. 이렇게 케이스가 나뉘면서 다른사람이 보았을때 혹은 미래의 내가 보았을때 코드가 쉽게 파악되지 않는 문제가 있었습니다.

Slide 78

Slide 78 text

ೠӒܶ੄झ౵ѱ౭௏٘

Slide 79

Slide 79 text

੉۞ೠ੉ਬ۽ CMS : CMS ? 4 : , . ? 몇가지 아쉬웠던 점이 있지만 기존에 사용하던 구조에서 break change를 이뤄냈다는 것과 가설을 실제 작은 서비스 레벨로 구현하여 검증했다는 점에 우선 의미를 두기로 했습니다.

Slide 80

Slide 80 text

੉۞ೠ੉ਬ۽ ( ! ) 하지만 역시 다 만들고 나서야 아까같은 스파게티 상태가 눈에밟혔습니다. 이를 개선하기 위해 다 부수고 다시 만드는 것은 일정상 불가능한 일이었으므로 이 프로젝트는 이대로 유지보수 하는것으로 하고, 묻고 다음 프로젝트에서 더블로 잘해보기로 했습니다.

Slide 81

Slide 81 text

/PX જ਷૘ৈ੿੄അ੤ ӝઓ ҳઑܳ ѐࢶೞ੗ח ೠߣ੄ ௾ Ѿबਸ ೮૑݅ ҅ࣘ೧ࢲ ѐࢶ੉ ೙ਃೞ׮ח Ѫਸ ݆੉ וԕणפ׮. જ਷ ૘ ৈ੿਷ അ੤ ૓೯઺ੑפ׮.

Slide 82

Slide 82 text

프로젝트개선의 현재에 대한 이야기를하기전에, 여러분은 만약 이사를 고려하신다면 어떤 점들을 고려하시나요? 저는 얼마전에 이사를 했는데 단순히 집의 모양새 뿐만 아니라 주변 교통이나 상권등도 이사에 있어 중요한 요소였습니다.

Slide 83

Slide 83 text

? 이는 프로젝트 구성시 고려하는 것과 별반 다르지 않습니다.

Slide 84

Slide 84 text

೐۽ં౟ܳ ҳࢿೞח ਃࣗܳ ଘ աৌ೧ࠁҊ, ݾ੸ী ݏѱ ੤ߓৌ ೞӝ. 그래서 두번째 프로젝트를 더블로 잘하기 위해 이렇게 칠판에 프로젝트의 전체 그림을 그렸었습니다.

Slide 85

Slide 85 text

이렇게 칠판에 열심히 적으면서 잡아본 현재구조는 이런 형태입니다. api, asset, domain, presentation, store와 util 폴더로 구성되어 있습니다.

Slide 86

Slide 86 text

index.ts api의 index에는 header, requestGet같은 공통적으로 사용할 함수를 만들어둡니다.

Slide 87

Slide 87 text

.ts api폴더 하위의 각 파일에서는 공통적으로 만들어 둔 함수를 이용해서 실제 api를 호출하는 함수를 만들어둡니다.

Slide 88

Slide 88 text

components / container / routes / view presentation쪽은 components, container, routes, view 크게 4개로 구성됩니다.

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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로 가지고 있습니다.

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

components / container / routes / view Route : component view View : container route쪽에서 routing을 담당하고, 여기에 매칭되는 component가 view입니다. view에서는 비즈니스 로직없이 layout만 담당하게 됩니다.

Slide 95

Slide 95 text

"OE , Project view data util api helper 아까 소개되었던 프로젝트의 5가지 구성요소 입니다.

Slide 96

Slide 96 text

Project view data util api helper "OE , : helper controller ! 1 (util ) view(container || component)

Slide 97

Slide 97 text

"OE , 모호했던 helper function에 대해서는 이렇게 정리했습니다. helper의 위치는 component혹은 container 하위의 controller라는 폴더에 위치해야 한다는 규 칙을 세웠습니다. util과는 다르게 view를 위한 함수이며 1회성에 가까운 역할을 한다고 정의했습니다.

Slide 98

Slide 98 text

Project view data util api helper "OE , : util , view !

Slide 99

Slide 99 text

"OE , 반대로 util은 특정 component나 container가 아니라 어디서든 범용적으로 사용가능해야 하며, 자연히 추상화 레벨이 높아야 합니다. 너무 많은 결정을 하는 함수는 util에 두지 않기로 했습니다.

Slide 100

Slide 100 text

࢜۽਍૘ : ( ) , ~ ! : ( ) 그리고, 새로운 집에 리덕스를 도입하고 공유하는 과정에서 팀원들이 새집증후군을 느꼈는데요,

Slide 101

Slide 101 text

࢜۽਍૘ : ! : 이를 해소할 무언가가 필요했습니다. 여기서부턴 아까 말씀드렸던 리덕스를 쓰면서 불편했던점에 대한 꿀팁정도로 이해해주시면 좋을것 같습니다.

Slide 102

Slide 102 text

࢜૘ૐറҵృ஖ܻ؋झಞ action, reducer, middleware . redux . . 아까 액션, 리듀서, 미들웨어를 정의하는데있어 비슷한 코드를 매번 적어주어야 한다는 불편함이 있었는데요, 생각해보니 redux는 잘못이 없고 제가 조금 잘못사용하고 있지 않나.. 라는 생각이 들었습니다. 오른쪽 사진이 리덕스 새집증후군을 해결하기 위한 actionUtil 함수인데요,

Slide 103

Slide 103 text

redux . . const action = createAsyncAction(PREFIX) action, reducer, middleware . const epic = createAsyncEpic(PREFIX, apiCallFunction) ࢜૘ૐറҵృ஖ܻ؋झಞ

Slide 104

Slide 104 text

이를 자세히 보자면 이런 구조입니다. createAsynAction이라는 유틸성 함수를 정의하고 이 함수는 3개의 프리픽스와 액션 함수를 리턴 합니다. 사용하는 쪽에서는 함수 하나만 호출하면 바로 액션정의가 끝나게 됩니다. 미들웨어 또한 이런식으로 작성해서 중복코드를 줄였습니다.

Slide 105

Slide 105 text

reducer switch - case action, reducer, middleware . ࢜૘ૐറҵృ஖ܻ؋झಞ 리듀서 쪽에서도 기존의 switch-case 문법을 걷어냈습니다. 여기 보시는건 로딩 리듀서인데요, fetchState를 관리하는 것은 모든 리듀서에서 필요했던 요소여서 로딩 리듀서 또한 유틸성으로 분리했습니다.

Slide 106

Slide 106 text

: redux actionUtil ! 
 ! 이렇게 액션유틸을 만들고 팀원들에게 공유해본결과,

Slide 107

Slide 107 text

초기셋팅 비용이 많이 들고 중복코드가 많아서 괴로웠던 점이 어느정도 해소 되었다는 답을 들을 수 있었습니다.

Slide 108

Slide 108 text

( ) 이런 점들을 고쳐나가며 프로젝트를 만들어나가고 있는데, 아직까지는 괜찮은 구조인것 같습니다. 각각이 어디에 있는지 명확하며 리뷰하기에 쉬운 코드를 만들기 위해 열심히 노력하고 있습니다.

Slide 109

Slide 109 text

/ߣ੄੉ࢎҗ੿ਸѻਵݴʜ ੉ࢎܳೞח੉ਬ ੉ۧѱ ௼ѱח ೠߣ, ੘ѱೠߣ / ୨ فߣ੄ ҳઑ ѐࢶҗ੿ਸ ࣗѐ೧٘۷חؘਃ, ઁо ҳઑ Ҋ஖ӝо ஂ޷о ইפۄݶ ৵ ੗Բ ੉ࢎܳ ೞחѤ૑ ੄ޙ੼ਸ о૑न ٜ࠙ب ҅पѪ эणפ׮.

Slide 110

Slide 110 text

#FTU1SBDUJDFۆ 바로 BestPractice를 찾기 위해서 입니다. 그런데 Best Practice란 무엇일까요?

Slide 111

Slide 111 text

#FTU1SBDUJDFۆ ❌ 저는 아직 못찾았고, 사실 그런건 없는게 아닌가.. 라는 생각도 듭니다.

Slide 112

Slide 112 text

#FTU1SBDUJDFۆ 왜냐하면, ‘좋다’ 라는 정의는 팀의 상황과 서비스에 따라 달라질 수 있기 때문입니다. 그래서, 좋은 패턴을 위해서는, 팀 구성원이 머리를 맞대고 합의해나가는 과정이 필요합니다. 문제점을 의식하고 공유하여 우리팀에 맞는 더 나은 구조를 계속 찾아가보고 있습니다.

Slide 113

Slide 113 text

: ( ) : ( ) ! 그리고, 저장소가 분리되고 매번 구조를 Set up하는 것은 비용이 많이들고 힘든 일이 될 수 있습니다. 이를 개선하기 위해 Project Scaffolding 도구를 만들었습니다.

Slide 114

Slide 114 text

sample project template repository를 만들고, 각 폴더에 변경된 구조를 아카이빙 하기로 했습니다. github api를 활용해서 api call까지 하는 형태의 샘플 프로젝트를 만들어서 아카이빙 하고 있습니다.

Slide 115

Slide 115 text

. sample project sca olding 이렇게 샘플프로젝트를 만들면 새로운 구조에 대해서 누구나 쉽고 간단하게 검증해볼 수 있습니다. 저희 팀에서 관리하는 대부분의 프로젝트는 api response를 이용하여 이를 가공하는 형태의 프로젝트 이기 때문이죠. 덤으로, 스캐폴딩도구도 만들 수있으니 서비스별로 저장소를 분리한 저희팀에 유용한 방식이 아닌가.. 하고 생각하고 있습니다.

Slide 116

Slide 116 text

( ) 이런저런 과정을 거쳐서 아직 구조는 조정중에 있습니다. 지금 사용하고 있는 구조역시, 결국 불편한 점이 있다면 다른 구조를 선택할 수 있습니다. 더 많은 검증이 필요하고 계속해서 불편한점에 귀기울이며 프로젝트는 계속 구조조정 중입니다.

Slide 117

Slide 117 text

хࢎ೤פ׮ ੉ࣗ৔]TPZPVOH!SBJOJTUDPN]IUUQTTPTPEFW