NHN FORWARD 2019 후기
27 Nov 20192018년에 처음 기술 컨퍼런스를 개최하고 올해도 두번째를 이어가고 있는 NHN FORWARD에 참가하게 되었다. 듣고 싶은 세션들이 있었는데 추첨 메일을 받았을 때 정말 기뻤다. 대부분의 큰 컨퍼런스는 행사가 진행된 삼성역에서 가까운 코엑스에서 많이 열리곤 하는데 파르나스 호텔에서 진행하면서 식사와 다과도 호텔에서 제공해주는 점이 매우 마음에 들었다. 식사는 800명 한정으로 제공해주는 거였는데 다행히 일찍 가서 식사권을 받을 수 있었다.:smile: 오늘 참가하면서 내년의 NHN FORWARD도 기대하게 되었다.
대부분 들을 세션을 정해놨는데 알고리즘에 대한 발표인 CPU full load 개선기는 내가 이해할 수 있을까라는 걱정에 조금 고민하였지만 정말 재밌게 들은 세션 중 하나였다. 마지막 세션에는 피곤하여 듣고 싶던 세션을 포기하고 일찍 집에 왔다. 나중에 발표 자료가 올라오면 찾아볼 예정이다.
발표명 | 발표자 |
---|---|
‘깃’깔나는 Git 워크플로 알아보기 | 신승엽/NHN Edu |
PAYCO 쇼핑 마이크로서비스 아키텍처(MSA) 전환기 | 이한진/NHN |
HTTP API 설계, 후회, 고민 | 이경환/NHN |
Spring JPA의 사실과 오해 | 신동민/NHN |
쿠폰듀스X101: 가장 좋은 쿠폰을 픽하려다 만난 CPU full load 개선기 | 황도영/NHN |
‘깃’깔나는 Git 워크플로 알아보기(트랙 1)
주요 git 워크 플로우 살펴보기
Git Flow
- A successful git branching model(Vincent Driessen 2010.1.5)
- 본인의 프로젝트에 성공적으로 사용했다며 소개
- Main branch
- master branch: 배포된 코드를 모아두며 tag 사용
- develop branch: 다음에 배포할 코드 모아두고 후에 master 브랜치에 merge
- Supporting branch
- 병렬 업무 가능
- 필요할 때 생성, 역할을 완료하면 삭제
- feature branch: 하나의 기능을 개발하기 위한 브랜치. develop에서 생성 후 완료하면 다시 merge. fast forward 하지 않도록 주의
- release branch: 소프트웨어 배포를 하기 위해 준비하는 브랜치. 배포 전 버그 등 수정. 배포 준비가 완료되면 master와 develop에 merge
- hotfix branch: 배포 버전에 문제가 생길 경우 사용. master에서 생성. 완료되면 master와 develop에 merge.
GitHub Flow
- GitHub Flow(scott chacon)
- Deploying at GitHub(jake douglas)
- git flow는 대부분의 케이스에서 너무 복잡
- 워크 플로우를 이해하기 쉽게 되어 실수가 없어지고 더이상 헤매지 않게 됌
- GitHub 의 개발 철학도 함께 설명
- master: 항상 stable 해야함.
- topic branch
- feature branch와 동일하며 기능 개발
- 브랜치 이름을 기능을 설명하는 명확한 이름으로 짓도록 함
- 커밋은 업무가 완료되지 않았어도 꾸준히 push 하도록 하여 구성원 모두를 끊임없이 커뮤니케이션 할 수 있도록 함. 브랜치 모델을 보면 업무를 파악할 수 있음
- 기능을 구현하면서 pull request 개설
- 코드 리뷰, 논의 진행
- 개발이 완료되면 배포에 사용하도록 함(bot 이용)
- CI 빌드 통과
- lock 가능
- master 브랜치 최신 커밋 존재
- 과정 거친 후 배포 시작
- 배포 후 예외, 오류 확인
- 배포 후 이상 없으면 merge. pull request 이용
GitLab Flow
- GitLab Flow(sytse sijbrandij)
- Git Flow는 너무 복잡
- GitHub Flow는 너무 간단
- GitLab Flow는 여러가지 케이스에서의 모범 사례 소개
- 지속적 배포가 어려울 때
- master
- production
- 배포할 코드
- 자동 배포하도록 설정하면 정확한 시각 파악 가능
- tag 생성하면 배포 시간 파악 용이
- 환경별 배포가 필요할 때
- 각 환경에서 테스트를 통과한 경우만 다음 단계로 merge 가능
- master
- staging에 자동 배포
- pre-production에 merge
- pre-production
- production에 merge
- production
- release software일 때
- master
- stable version 브랜치 생성, tag로 버전 표기
- bug-fix 브랜치 생성 후 merge
- master
- 개발 단계에서 지킬 규칙
- 개발이 완료되지 않더라도 중간 결과를 팀 내에 공유
- 커밋을 자주하고 푸시도 자주하라
- merge 전에 테스트
우리는 이렇게 해요!
브랜치 전략
- 상황
- 단기간의 배포 일정
- 장기간의 배포 일정
- 코드 네임 부여해서 관리
- 같은 시간에 여러 배포 일정의 작업 필요
- Git Flow 기반으로 develop 브랜치를 단기간, 장기간 브랜치로 나누어서 진행
- 각 개발 일정 브랜치에서 각 feature 브랜치 생성
- feature 브랜치는 pull request 생성하고 리뷰 후 merge
- 개발 일정 브랜치가 완료되면 QA진행
- QA완료 후, 개발 일정 브랜치를 master와 develop에 각각 merge 하고 master에는 tag 생성
- 나머지 개발일정 브랜치를 rebase 해줌. merge 커밋 유지
- 각 feature 브랜치도 rebase 진행
- hotfix가 필요할 때는 gitflow의 절차 따름. 완료 후 develop와 master에 각각 merge. pull request 상에서 코드 리뷰와 테스트만 진행. Git Flow 도구를 이용하여 merge
- hotfix merge 후, 각 개발일정과 feature 브랜치 rebase
개발 플로우(Github 기반)
- 업무 생성
- 개발 진행
- 개발 완료 후 pull request 생성
- 몇 가지 조건을 통과해야만 merge 할 수 있도록 설정
- 코드 리뷰
- 테스트
- 몇 가지 조건을 통과해야만 merge 할 수 있도록 설정
- 장애 발생 시 처리
- jenkins를 이용한 자동 테스트
- GitHub pull request builder 플러그인
- Sonar Qube 이용한 정적 분석
- 버그, 코드 취약점 등을 탐지하는 자동 리뷰 툴
- 사용하지 않는 변수나 import 등 탐지
- pull request merge
- GitHub PR + jenkins + Sonar Qube
- 기계적인 테스트, 코드 리뷰 자동가능
- 사람은 좀 더 비즈니스 로직에 집중하여 리뷰 가능
PAYCO 쇼핑 마이크로서비스 아키텍처(MSA) 전환기(트랙 5)
PAYCO shopping?
기본 구조
- 파트너 쇼핑몰로 상품 수집(색인 서버)
- 검색 서버를 통해 페이코쇼핑 웹 서버로 상품 질의
- payco id 인증 후 파트너 쇼핑몰 이용
- payco 결제
- 주문 수집 서버에서 주문
문제 상황
- NCP(NHN Commerce Platform)
- SaaS 지향
- admin, FE 제공
- 클라우드 기반의 경우, 오토 스케일 안정적으로 제공
- 프로젝트 모듈(Java 8, Spring, Gradle 기반)
- 추가 기능
- 추가되면서 common module이 커짐
- 빌드 시간 증가
- 생산성 감소
- 서비스 분리 결정
- 새로은 REPO 생성 후 복사 붙이기
- 배포시간? 섞여있는 소스? 시스템 업그레이드?
- MSA 결정
- 모듈 간 낮은 커플링
- 독립적 배포
- 아키텍처의 유연함
- 새로운 언어 선택: 코틀린
MSA 시작
- 현재 상황 파악 및 계획
- 범위, 도입 방법
- 기술 스택 조사
- 트렌드, 맞는 기술 스택
- 현재 사용하고 있는 기능의 전수 조사
- 기능 기준 RESTful API 목록 추출
- 도메인 구별
- 결과적으로 4개의 MICROSERVICE로 분할
- API Gateway
- Netflix ZUUL
- Service Discovery
- Netflix EUREKA
- Spring Cloud Netflix
- Spring Cloud gateway
- Netflix ZUUL에서 변경
- apache zookeeper
- Netflix EUREKA에서 변경
- Spring Cloud config server
- cloud configuration
- github에 자동 반영
- refresh API call
- containerize
- docker
- nexus repository manager 3 이용
- docker internal registry(private) 사용 가능
- docker proxy registry 사용 가능
- jenkins
- ansible
- docker
- monitoring
- nSight
- Scouter - Zipkin
- Grafana
- Scouter - xlog
- 문서화
- swagger based documentation
- swagger annotation
- 장점
- api 문서의 현행화가 쉬움
- 이해하기 쉽고 개발도 쉬움
- 주석을 대체할 수 있음
- 단점
- 개발 로직과 상관없는 코드
- spec file만 별도로 생성할 수 없음
- 분산 환경에서의 문서 통합이 어려움
- functional endpoint 방식에서 api 문서화 불가능
- 장점
- 보완 대책
- Spring REST docs
- OpenAPI 3.0
- swagger UI
HTTP API 설계, 후회, 고민(트랙 1)
RESTful API
- 당시에 따른 규칙
- 슬래스 구분자(/)는 계층 관계를 나타내는 데 사용
- 도큐먼트 이름으로는 단수 명사를 사용
- 컬렉션 이름으로는 복수 명사
- 컨트롤러 이름은 동사나 동사구
- crud 기능을 나타내는 것은 uri에 사용하지 않음
- put 메서드는 리소스를 삽입하거나 저장된 리소스를 갱신하는 데 사용
- 리소스 계층도
- 리소스 계층도에 맞게 url 설계
- file
- content negotiation header 사용
- 메일 읽음 표시
- preview라는 query string보냄(표준은 아님)
- 표준 방식은 put으로 보내기
- PUT vs. PATCH
- PUT을 PARTIAL UPDATE 가능하게 정의하여 사용 중
- NULL이 의미를 가지는 필드 업데이트 등에 어려움이 있음
- PATCH를 잘 살펴보지 않은 것을 후회중
- PARTIAL UPDATE
- RFC 6902
- RFC 7386
- PUT을 PARTIAL UPDATE 가능하게 정의하여 사용 중
- 미리 잘 정해두면 좋은 것
- 리소스에 어울리는 단어들 - 비슷한 뜻을 가진 단어들이 혼용되는 것을 방지
- flag성 이름 규칙 정하기
- 날짜, 시간 필드명
- 날짜 시간값 포맷
- query parameter 이름도 가능한 정확한 표현으로 짓는 것이 좋음
Spring JPA의 사실과 오해 (트랙 1)
Entity 매핑, 연관관계 매핑
- Entity 매핑
- Entity : JPA 를 이용해서 데이터베이스 테이블과 매핑할 클래스
- Entity 매핑: Entity 클래스에 데이터베이스 테이블과 컬럼, 기본 키, 외래 키 등을 설정하는 것
- 연관관계 매핑
- Entity들은 대부분의 경우 다른 Entity들과 연관관계를 가짐
- 데이터베이스 테이블은 외래키로 join을 이용해서 관계 테이블 참조
- 다중성
- @OneToOne
- @OneToMany
- @ManyToOne
- @ManyToMany
- 방향성
- 단방향
- 양방향
- 단방향 vs 양방향
- 사실상 단방향 매핑만으로 연관관계 매핑은 이미 완료
- 단방향 매핑에 비해 양방향 매핑은 복잡하고 객체에서 양쪽 방향 모두 관리해야 함
- 양방향 매핑은 단방향 매핑에 비해 반대 방향으로의 객체 그래프 탐색 기능이 추가된 것 뿐
- 대개의 경우 단방향으로도 충분
- 영속성 전이
- Entity의 영속성상태 변화를 연관된 Entity에도 함께 적용하는 것
- 연관관계의 다중성 지정시 cascade 속성으로 설정
- PERSIST, REMOVE, DETACH, REFRESH, MERGE, ALL
- 오해 - 양방향 매핑보다 단방향 매핑이 좋다
- 일대다 단방향 연관관계 매핑에서 영속성 전이를 통한 INSERT 시
- 일대다 관계의 외래키 지정을 위해 추가적인 UPDATE 쿼리가 발생하는 문제
- 이 경우 오히려 일대다 양방향 연관관계로 변경하면 추가적 UPDATE 쿼리가 없어짐
- 일대다 단방향 연관관계 매핑에서 영속성 전이를 통한 INSERT 시
- FETCH 전략
- FetchType.EAGER
- FetchType.LAZY
- 기본 FETCH 전략
- *-ToOne: FetchType.EAGER
- *-ToMany: FetchType.LAZY
- N + 1 문제
- ENTITY에 대해 하나의 쿼리로 N개의 레코드를 가져올 때 연관관계 Entity 를 가져오기 위해 쿼리를 N번 추가적으로 수행하는 문제
- 해결방법
- Fetch Join
- Entity Graph
- Spring Data JPA Repository
- Spring Data project에서 제공
- 오해 - JPA Repository 메서드와 JOIN
- JPA는 데이터베이스 테이블 간의 관계를 Entity 클래스의 속성으로 모델링
- JPA Repository 메서드는 언더스코어(_)를 통해 내부 속서앖에 접근 가능
- JPA Repository 메서드에서도 다양한 DTO Projection 지원
쿠폰듀스X101: 가장 좋은 쿠폰을 픽하려다 만난 CPU full load 개선기(트랙 2)
무엇이 문제일까?
Heap graph
- 단순 트래픽 문제인지 알 수 없음
CPU graph
- 가끔 cpu가 매우 바쁨
- 50% 또는 100% 사용
Thread dump
- 무언가가 반복 호출됨
왜 느릴까?
- NCP는 다양한 쿠폰 기능 제공
- 여러 조건과 상황이 발생
- 상품-쿠폰 매칭 이슈
- 탐욕 알고리즘 적용이 어려움
- 다양한 쿠폰들로 발생되는 이슈
- 각각 다른 종류의 쿠폰은 변치않은 판매 정가에 의존함
- 각각의 쿠폰은 서로에게 영향을 미치지 않으며 독립적인 관계
- 상품 할인가에 의존하는 쿠폰은 다른 쿠폰에게 영향을 미치며 의존적인 관계
- DFS - 사용자 입력에 너무나 큰 성능 편차를 보임
- 상품 개수가 늘어나면서 연산 횟수가 폭발적으로 증가
해결해보자!
아이디어 1 - 쿠폰들 의존관계 끊기
- 쿠폰들이 서로 의존적이라 문제가 너무 복잡함
- 가장 좋은 것을 찾는 것이 아닌 적당히 좋은 것을 찾는 문제가 됌
- O(M^P)로 개선 (M 쿠폰 수, P 제품 수)
아이디어 2 - M^p 알고리즘 개선
- 할당 문제
- 헝가리안 알고리즘 O(K^3)사용
- K x K 정방 행렬로 만들어준다.
- 행렬의 각 행과 열에서 최소값을 각각 뺀다
- 최소 라인으로 0의 성분의 셀을 지운다. 이 라인이 k개 라면 답이 존재
- 남은 수 중 0이 아닌 가장 작은 수로 각각 뺀다
- 음수 발생 시, 0 이상의 수가 되도록 다시 더한다
- 서로 겹치지 않는 0의 위치가 해가 된다.
개선 후 결과
- 100개의 제품과 쿠폰으로 적용한 결과 짧은 시간 내에 완료