NHN 포워드 참석 후기.
- 사람 많음
- 점심밥을 무료로, 발표 세션 내부 홀에서 먹음
- 유익하였음
- 아는 것이 힘이다.
1. 점진적인 프론트엔드 프레임워크 교체 (앵귤러 → 뷰)
- nhn 에 Dooray! 라는 협업관리 도구가 있다.
- Dooray! 는 SPA 형태.
- 처음에는 앵귤러로 시작했으나 이후에 뷰로 전환하기로 결정하였음
- Why ?
- 써드파티 패키지 지원이 약함
- 새로운 기능을 사용할 수 없음
- 유연성 및 확장성 부족
- Why 점진적으로 프레임워크를 전환하는가 ?
- 전면 교체는 리스크가 높기 때문이다.
- 점진적으로 수행하면 리스크가 상대적으로 낮다 : 빠른 적용 및 배포, 분산된 교체 비용
- Why ?
- 프레임워크를 교체하기 이전에 필수로 고려해야할 사항
- 사전에 기능을 파악한다.
- 기획서, QA 시트, 설계문서, 기능파악
- 서비스의 기획서를 확보
- QA 테스트 케이스 문서 확보
- 코드레벨에서 기능을 파악 (문서를 통해서 드러나지 않는 것들을 파악하기 위함)
- 기획서, QA 시트, 설계문서, 기능파악
- 사전에 기능을 파악한다.
- 웹 페이지 구조 결정 과정 : 점진적으로 프레임워크를 전환하기 위한 고민
- 세가지를 고민하였음
- (1) SPA 포기 및 별도 페이지 분리
- (2) SPA 유지 및 iframe 사용
- (3) 앵귤러 JS 내부에서 뷰 컴포넌트 삽입
- 장점 : SPA 유지, 특정영역만 뷰 전환 용이, 멀티 인스턴스 가능, iframe 대비 가벼움, 두 프레임워크 간 데이터 공유 등이 쉬움
- 단점 : JS와 CSS 용량이 늘어남, CSS 충돌이 날 가능성이 있음
- 앵귤러와 뷰가 서로 통신하는 방법
- (1) 뷰의 리액티브 시스템의 활용 : data & props & watch
- (2) vuex 로 데이터 전달 : dispatch & commit
- (3) 뷰 내부에서 앵귤러로 데이터 전달 : vuex.store 의 subscribeAction 사용
- 프레임워크 전환 이후 테스트
- 회귀테스트 : 해당 테스트를 진행함으로써 사전에 기능이 구현되었는지 유무를 파악
- 리소스 크기 및 요청 수 비교
- 로딩 성능 비교
- DCL (DomContentLoaded) : HTML 문서가 완전히 로드 및 파싱이 완료된 시점
- L (Load) : 모든 리소스 로딩 완료 시점
- FMP (First Meaningful Paint) : 화면에 렌더링되는 시점
- 결론적으로 말하면 Dooray! 는 두 프레임워크가 공존하고 있는 상태 : 앵귤러, 뷰
- 기억해둬야할 키워드 : 스토리 북, 마이크로 프론트엔드 전략
2. DDD-Lite@Spring
- 애플리케이션 복잡성 때문에 고통받는 개발자들을 대상.
- 복잡성과 위기
- 기술은 발전하고 왜 우리의 프로젝트는 힘들까?
- 근본적으로 애플리케이션이 복잡하기 때문이다. (도메인, 기술 등의 복잡성)
- 이를 DDD 로 극복하자.
- 어떤 이해당사자가 있고, 우리는 어떤 문제를 해결해야기위해서 문제공간을 해결공간으로 만들어야 한다.
- 아주 큰 도메인을 서브 도메인으로 분리하는데 이를 DDD 전략패턴이라고 칭하자.
- DDD 의 전술적 패턴 : 작은 그림 (DDD-Lite)
- DDD 의 전략적 패턴 : 큰 그림
- 사실상 DDD-Lite 는 DDD 의 전술적 패턴의 일부이지만 발표세션에서는 일부라고 칭하지 않고 DDD 전술적 패턴 그 자체로 설명하도록 하겠다. (듣긴 들었으나 자세히 못들음..)
- Dooray! 위키 ( Dooray!는 nhn의 협업관리 도구 )
- Dooray 관계 파악
- 위키 서비스에 집중
- 위키 엔티티
- DDD 에서는 모델링을 할 떄 행위가 중요 ! (도메인에 행위가 명확히 표현될 수 있어야 한다.)
- 속성은 행위가 필요할 때 추가
- 개체 간의 연관관계는 어떤 행위에 필요한 특정한 관계가 있다면 그 때 관계를 맺도록 한다.
- JPA 로 생각하면 바로 양방향을 맺지말고 단방향을 우선적으로 수행하고 이후 필요하면 양방향으로 수행한다.
- Dooray! 위키 엔티티에 대한 명세
- - 페이지 아이디, 위키 아이디 등등
- create() 라는 행위가 결정되고 이후 필요한 속성을 추가한다. (행위가 먼저 결정되고 이후에 속성을 추가)
- Dooray! 위키에 이력관리가 필요하다.
- 새로운 요구사항
- 새로운 엔티티가 필요하고 이에따라 스키마를 작성
- 엔티티는 식별성이 있고, 생명주기가 있으며, 행위가 있다. 또한 그와 관련된 속성이 있으며 이는 결국 응집력이라고 부른다.
- 중략... ( 나에겐 생소한 내용이 많았는데, 좋은 내용이 많았다. 이후 올라오는 자료를 보는걸 추천 )
- 도메인 모델을 풍부하게 만들어주어야 한다. ( Rich Domain Model )
- 멱등성을 지킨다. ( 멱등성 : 여러번 연산을 적용하여도 그 결과가 달라지지 않는 성질 )
- POJO vs Spring Data JPA
- 왠만하면 Spring Data JPA 를 사용하지 않으려고 한다. 왜냐하면 스프링에서 제공하는 그 편리함에 의존적이고 싶지 않기 때문이다. 그래서 직접 레파지토리를 만든다. 그러다가 그 레파지토리가 Spring Data JPA 에서 제공하는 인터페이스와 유사해지면 그 땐 trade off 한다. 이에 관련해서 고민을 해봐야 함.
- 아키텍처( 생소했다. 이후 올라오는 자료를 보는것 추천 )
- 헥사고날 아키텍처
- 도메인이 인프라에 의존하게 되고, 도메인적 관심사와 인프라적 관심사가 섞인다.
- 결론적으로 말하면 기술보다 도메인이 먼저다.
3. HTTP 설계, 후회, 고민
- Dooray! 서비스 개발시 HTTP API 설계의 고민과 흔적들.
- 리소스 계층구조
- 전체 업무를 모아서 보고 싶다.
- (최초) : GET /orgs/{org-id}/projects/{project-id}/tasks
- (1차 변경) : GET /orgs/{org-id}/projects/*/tasks
- (2차 변경) : GET /orgs/{org-id}/tasks
- 요청 URL 안에 `*` 가 들어가는게 알맞는가?
- 계층을 한단계 앞당기자.
- 업무를 작성하면서 임시저장 기능이 필요하다.
- 리소스 계층도에 변화가 생김
- 최초 : {조직} --> {프로젝트} --> {업무|임시저장}
- 조직은 1레벨, 프로젝트는 2레벨, 업무 및 임시저장은 3레벨 계층
- 변경 : {조직} --> {프로젝트 | 임시저장} --> {업무}
- 업무를 작성하면 계속 임시저장이 되다 이후에 완료되는 시점에 POST 요청에 임시저장 ID 이 포함
- 임시저장의 레벨을 한단계 앞으로 옮김
- 최초 : {조직} --> {프로젝트} --> {업무|임시저장}
- 리소스 계층도에 변화가 생김
- 중략...
- 업무를 다른 프로젝트로 옮기고 싶다.
- CRUD 이외의 나머지 액션을 표현할 일이 있으면 uri 경로에 `동사구` 를 붙이자
- /../projects/{project-id}/tasks/{numbers}/move
- HTTP 설계에 대한 고민
- HTTP Cache 적용 쉬울까?
- 권한관리 : API 단위로 권한을 부여하는 방법은 쉽다. 하지만 필드별로 권한이 다른 경우 ?
- 리소스 생성에 다른 부수 효과가 있는 경우
- 리소스를 생성할까?
- 요청에 값을 포함시킬까?
- 리소스에 잘 어울리는 표현들을 정하자
- create, register
- update, edit, modify
- delete, remove
- use, enable, activate
4. Spring JPA 의 사실과 오해
- 소프트웨어 공학의 사실과 오해에서 해당 발표 세션의 타이틀을 가져왔음
- 연관관계 매핑
- 일대다에서 단방향을 쓰면 update 쿼리가 두 번 날라가기 떄문에 양방향이 더 나을 수 있음.
- N + 1 문제에 대한 흔히들 하는 오해
- N+1 문제는 eager fetch 전략 때문에 발생한다 ?
- Fetch 전략을 LAZY 로 설정했더라도 연관 Entity 를 참조하면 그 순간 추가적인 쿼리가 수행
- findAll() 메소드는 N+ 1 문제를 발생시키지 않는다 ?
- 단일 레코드 조회가 아닌 경우, 반환된 레코드 하나하나에 대해 Entity 에 설정된 Fetch 전략을 적용해서 연관 엔티티를 가져온다. 그렇기 때문에 findAll() 메소드 호출도 역시 이 과정에서 N + 1 문제가 발생.
- N+1 문제는 eager fetch 전략 때문에 발생한다 ?
- Spring Data JPA Repository
- JPA Repository 메소드와 JOIN
- JPA Repository 메소드에서는 "_" (언더스코어) 를 통해 내부 속성 값을 접근할 수 있다.
- Page vs Slice 둘의 차이점
- Page<Member> getAllByName (String name, Pageable pageable)
- select * from Members where name = {name} offset {offset} limit {limit}
- select count(*) from Members where name = {name}
- Slice<Member> readAllByName (String name, Pageable pageable)
- select * from Members where name = {name} offset {offset} limit {limit_plus_1}
- Page<Member> getAllByName (String name, Pageable pageable)
- JPA Repository 메소드와 DTO Projection
- Class 기반 DTO Projection
- Interface 기반 DTO Projection
- Dynamic Projection
- JPA Repository 메소드와 JOIN
추가적으로 게임서버 200만 동접을 위한 MySQL 도 들었다. 하지만 피곤한 상태로 들었기도 하고, 아는 부분이 적어서 그렇게 집중있게 듣지 못했다. 다만 이번 컨퍼런스 참여를 통해 느끼게 된 것은 정말 아는 만큼 들리고 아는 만큼 보인다는 것이다. 많은 부분을 이해하고 싶었지만 그러지 못해서 다소 아쉬웠다. 다음번 참여에는 더 많이 들리고 보이고 이해할 수 있었으면 좋겠다.
'공사의 경계' 카테고리의 다른 글
20200802 [일상] 마리모 (0) | 2020.08.02 |
---|---|
2019 [회고] 2019년 한 해 돌아보기. (0) | 2019.12.29 |
20190630 Google I/O 2019 (0) | 2019.06.30 |
20190401 책 구매 (0) | 2019.04.01 |
20190322 첫 퇴사를 하기 이전에 이런저런 생각들. (0) | 2019.03.22 |