NHN 포워드 참석 후기.

  • 사람 많음
  • 점심밥을 무료로, 발표 세션 내부 홀에서 먹음
  • 유익하였음
  • 아는 것이 힘이다.

1. 점진적인 프론트엔드 프레임워크 교체 (앵귤러 → 뷰)

  • nhn 에 Dooray! 라는 협업관리 도구가 있다.
  • Dooray! 는 SPA 형태.
  • 처음에는 앵귤러로 시작했으나 이후에 뷰로 전환하기로 결정하였음
    • Why ?
      • 써드파티 패키지 지원이 약함
      • 새로운 기능을 사용할 수 없음
      • 유연성 및 확장성 부족
    • Why 점진적으로 프레임워크를 전환하는가 ?
      • 전면 교체는 리스크가 높기 때문이다.
      • 점진적으로 수행하면 리스크가 상대적으로 낮다 : 빠른 적용 및 배포, 분산된 교체 비용
  • 프레임워크를 교체하기 이전에 필수로 고려해야할 사항
    • 사전에 기능을 파악한다.
      • 기획서, 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 문제가 발생.
  • 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}
    • JPA Repository 메소드와 DTO Projection
      • Class 기반 DTO Projection
      • Interface 기반 DTO Projection
      • Dynamic Projection

추가적으로 게임서버 200만 동접을 위한 MySQL 도 들었다. 하지만 피곤한 상태로 들었기도 하고, 아는 부분이 적어서 그렇게 집중있게 듣지 못했다. 다만 이번 컨퍼런스 참여를 통해 느끼게 된 것은 정말 아는 만큼 들리고 아는 만큼 보인다는 것이다. 많은 부분을 이해하고 싶었지만 그러지 못해서 다소 아쉬웠다. 다음번 참여에는 더 많이 들리고 보이고 이해할 수 있었으면 좋겠다.

Posted by doubler
,