티스토리 뷰

Experience/2019

NHN FORWARD 2019 후기

nroo 2019. 11. 28. 11:46

NHN TOAST FORWARD 2019

2019.11.27

한줄 후기

최근에 가봤던 컨퍼런스중에 실무와 가까이 있다고 생각되는 주제들이 가장 많았습니다. 이미 팀에서 도입해서 사용하고 있거나, 우리가 내년에 개선하고자 하는 것들에 대한 주제 위주로 들었는데요, 우리와 비슷한 고민들을 하고 있는 것 같았습니다.



1. '깃' 깔나는 Git 워크플로우 알아보기

NHN Edu 서버개발팀 - 신승엽


주요 Git 워크플로우 살펴보기

  • Git flow

    • 항상 존재하는 브랜치

      • master 브랜치

      • develop 브랜치

    • 서포팅 브랜치

      • 필요할 때 생성 후 삭제하는 브랜치

      • feature 브랜치

        • 특정 feature의 개발이 완료되면 다시 develop으로 merge 이때, fast forward 하지 않도록 주의

      • release 브랜치

        • feature들이 포함된 develop 에서 따고, 릴리즈 후엔 master와 develop에서 merge

      • hotfix 브랜치

        • master에서 출발

  • GitHub flow

    • Git glow는 대부분의 케이스에서는 너무 복잡하다

    • 사람들이 워크 플로우를 이해하기 쉽게 되어 실수가 없어지고 헤메지 않게 되었다고 설명함

    • Master 브랜치가 항상 stable한 상태여야 한다. 현재 master 그대로 배포되어도 이상하지 않은 상태로 유지해야 한다.

    • 새로운 기능을 개발할 때는, Topic 브랜치를 master 브랜치에서 딴다.

      • 기능 개발

      • Pull request 개설

      • 코드 리뷰 / 논의

      • 완료된 토픽 브랜치를 그대로 배포한다.

        • 토픽 브랜치 배포시 반드시 CI 빌드를 통과해야 하며

        • 락이 가능하다

        • Master 브랜치의 최신 커밋이 존재하는지 확인해서 충돌을 막는다.

      • 배포 후 이상없다면, master에서 해당 토픽 브랜치 merge

        • 이때 배포 락이 해제 된다.

  • GitLab flow

    • git flow는 너무 복잡하며, github flow는 너무 간단하다

    • 지속적인 배포가 어려울 때

      • github flow는 마스터를 실서버에 그대로 배포하는 컨셉인데, 대부분의 회사에서 불가능할 수 있다.

      • 이럴경우 production 브랜치를 관리하고, production에서 master를 merge해서 배포한다.

      • 배포버전 관리에 용이하다.

    • 환경별 배포가 필요할 때

      • master는 staging에 자동 배포되고

      • Pre-production, production 브랜치 두개를 관리할 수 있다.

    • 릴리즈 소프트웨어일 때

      • 마스터에서 버전별 stable 브랜치를 따서 배포한 후

      • 핫픽스의 경우 master에서 수정하고 각 버전의 stable 브랜치로 cherry pick 한다.

      • 이것은 마치 리눅스에서 hotfix 하고, 각 배포판에 적용하는 것과 같은 원리이다.

우리는 이렇게 해요

  • 브랜치 전략

    • 상황

      • 단기간의 배포 일정인 경우

        • 배포 날짜로 관리. develop-20191121 등

      • 장기간의 배포 일정인 경우

        • 코드네임으로 관리. develop-tomato 등

    • git flow base

    • develop 브랜치에서 위의 상황에 맞는 각 sub develop 브랜치를 따고

    • sub develop 브랜치 개발이 완료되면 QA를 진행한다.

    • 사실 이 정책의 경우 릴리즈 브랜치가 필요 없다. sub develop이 명확하다.

    • sub develop 개발 후 QA가 끝나서 배포가 완료되면, master와 develop에서 merge한다.

    • 나머지 sub develop을 현재 develop에 rebase 한다.

      • 연속되는 충돌이 있을 경우 -> 영상 참조

      • 같은 이름의 sub develop 브랜치를 새로 따고, 체리픽 활용

    • 핫픽스는 master에서 따서 완료후 master, develop merge

    • 핫픽스 브랜치 merge 후에 sub develop들을 develop에 다시 merge.

    • 현재 포털개발팀 TV줌파트에서 쓰는 방식과 매우 유사함. (릴리즈 브랜치 관리는 협업 환경에 따라 다르다고 생각함)

  • 개발 플로우


2. 레거시 웹 서비스 길들이기 : 서버 개발자의 SPA 적용기

NHN 클라우드 프레임워크 개발팀 - 최강훈

웹 개발은 백엔드와 프런트엔드로 나눠서 전문성을 가지고 개발하는 게 트렌드입니다. 그러나, 이미 서비스 중인 덩치 큰 일체형 웹 서비스를 나누는 건 막막하고 두려운 일입니다. 이 세션에서는 서버 개발자 관점에서 SPA를 도입하여 레거시 웹 서비스라는 괴물을 길들였던 경험과 고민을 공유하고자 합니다.

  1. 프롤로그: 무엇이 문제인가?

  2. 1막: 워밍업

  3. 2막: 본격 분리 작업

  4. 에필로그: 1년간 운영해보니...


프롤로그 : 무엇이 문제인가?

  • 일체형 웹 서비스(with MVC) 방식은 기능과 화면이 적을 땐 괜찮았다.

  • 점점 비대해지는 웹 서비스

    • 모두가 꺼리게 된 자바스크립트 코드

    • 페이지는 무거워지고 서스테이닝은 점점 어려워 진다.

  • 어떻게 하면 될까요?

    • SPA를 도입하자.

    • 웹 페이지 구현에 필요한 모든 정적 리소스를 최초 한번 다운 받고

    • 이후 필요한 데이터는 그때그때 비동기로 받아서 화면을 만든다.

    • 하나의 페이지만 존재하고 페이지 전환 및 구성을 자바스크립트로 구현한다.

      • 장점

        • 성능 개선

        • 불필요한 네트워크 통신이 없어 진다

      • 단점

        • 검색엔진 최적화에 어려움이 있다

      • 언제 쓰면 좋은가?

        • UI 구성이 복잡하고, 데이터가 많이 필요한 웹 페이지

        • 페이지 내에 정적인 요소보다 동적인 요소가 많은 경우

        • 로그인을 필수로 해야되는 경우(검색엔진 최적화가 불필요한 경우)

      • 개편에 앞서 동료와 협업 부서 설득하기.

워밍업

  • 무기를 고르자

    • 앵귤러, 리액트, vue.js

      • 뭐가 제일 좋을까?(X)

      • 뭐가 우리 조직에 잘 맞을까?(O)

      • 앵귤러

        • 모든걸 갖췄지만 러닝커브가 높다

      • 리액트

        • 강력한 기능을 가지고 있지만, 더 필요한 도구는 필요에 따라서 선택해야 한다.

      • vue.js

        • 최소한의 있을건 다 있고, 배우기 쉬우며, 빠르게 만들어 낼 수 있다.

  • 우리 팀은 어떤가?

    • 자바 개발자가 많고, 자바스크립트 개발 비중은 상대적으로 적은 편

  • Vue.js 를 골랐다

    • 러닝커브가 가장 낮고, 가이드 문서도 잘 되어 있다.

    • single file component로 개발하면 하나의 파일에서 컴포넌트 단위로 작업할 수 있다.(퍼블리싱 팀과 협업시 용이)

  • 많이 변해버린 자바스크립트

    • ES6 문법이다.

      • let, const

      • Arrow function

      • import, export

      • class

      • 등 구글링해서 10분만 보자.

    • webpack 설정은 어떻게?

      • 프론트엔드 프레임워크를 도입할때 최대 고비.

      • gradle 학습할때와 비슷한 느낌으로 공부하자..

본격 분리 작업

  • 프로젝트 구성

    • Back-end, Front-end 어떻게 구성할까?

    • 어떻게든 둘을 합쳐보자

      • Spring-boot-vuejs

        • 부트와 vuejs를 maven으로 한번에 빌드

    • 빌드 프로세스

      • 프론트 엔드 빌드

      • 백엔드 리소스 디렉토리에 copy

      • 백엔드 빌드

    • 모든 요청을 was에서 해결하기

      • static 파일인데도 was로 서비스 해야되나?

      • UI 변경이 잦은 편인데, 매번 백엔드 포함하서 빌드/배포 해야되나?

      • 전과 차리가 없는데?

      • UI요청과 API 요청을 구분하자

        • 리소스 요청은 웹서버에서

        • 나머지는 was에서

    • 결론

      • 굳이 둘을 섞어서 프로젝트를 구성할 필요가 없었다.

      • 모놀리스 개발을 하다보니 합치는 것에 강박이 있었던 것 같다.

      • 백엔드 프로젝트와 프론트엔드 프로젝트는 과감하게 나누자

  • API 호출 방식

    • 일체형 웹 서비스에서는?

      • 서버 렌더를 위해 API 명세에 맞는 VO클래스를 만들어야 한다.

      • 클라이언트 렌더링에서도 이렇게 해야 될까?

        • API Gateway 사용 - 많은 기능중 API 라우팅 기능에 집중

        • Zuul 사용

          • 부트에서 간단하게 적용 가능

      • 백엔드는 API Gateway의 역할을 충실하게 하게 된다.

        • 기존의 VO를 제거하고 json으로 쭉쭉 내리면 된다.

    • 결론

      • API Gateway를 적용하고 불필요한 중복 코드를 걷어내자.

  • 상태 관리

    • 프론트엔드의 복잡한 컴포넌트 간 데이터 전달

    • 컴포넌트간 의존 관계가 복잡하다.

      • 컴포넌트 통신이나 이벤트 버스로는 벅차다

      • 중략 ...

      • 상태 관리 라이브러리 vuex 사용하자는 결론

    • vuex 사용 팁

      • api 호출 횟수 줄여보기

        • lodash의 debounce를 적용하여 여러 번 호출해도 한 번만 호출되도록 구성

        • 이미 로드한 데이터라면 추가로 호출되지 않도록

      • 데이터 가공은 어떻게?

        • RxJS에는 데이터 가공을 위한 유용한 연산자가 많이 존재함

      • vuex의 state, getter는 컴포넌트뿐만 아니라 다른 곳에서도 호출이 가능하다.

        • router에서 사용하면 데이터 로딩이 완료된 다음에 화면을 노출할 수 있음(서버 렌더링 처럼 보이는 효과)

    • 결론

      • 상태관리 라이브러리 사용해서 중앙 집중식 데이터 관리 하자

  • 다국어 처리

    • Spring MVC에서 다국어 처리

      • Spring MessageSource 사용

      • 서버 렌더 방식이다 보니 언어 변경을 하려면 반드시 새로 고침

    • 이제 다국어 처리도 프론트엔드에서

      • vue-i18n 플러그인 사용

      • 새로고침 없이도 언어 변경 가능

    • 아직 남아있는 문제점

      • 문구 수정 요청이 잦다

      • 문구 수정을 위해서는 여전히 빌드/배포가 필요하다.

    • 해결 책

      • 메시지 서비스를 만들자

      • 다국어 메세지를 관리하는 통합 메세지 서비스

      • 동일한 key를 사용하여 4개 국어 저장

      • 빌드 배포를 하지 않더라도 메세지 서비스에 요청해서 처리할 수 있다.

  • 배포 환경에 따른 설정 값 처리

    • 프론트엔드에서는 배포 환경에 따라 설정값을 어떻게 처리?

      • development

      • production

    • 실무에서는?

      • 팀, 프로젝트마다 사용중인 환경설정이 제각각

    • webpack 설정 파일의 분리

      • 각 환경별로 webpack 설정 파일 분리

      • DefinePlugin 사용하여 각 환경별로 다른 설정값 추가

      • package.json에서 각 환경에 맞게 빌드 명령어 구성

에필로그 : 1년간 운영해보니...

  • 성능 측면

    • 확실히 줄어든 API 호출 횟수

    • 웹 리소스 최적화

      • 웹팩 빌드 최적화 되면서 리소스 용량 작아짐

  • 운영 측면

    • 빌드/배포는 꼭 필요할 때 필요한 모듈만

  • 개발 측면

    • 프론트엔드 프레임워크의 도입

      • 규칙과 일관성이 생긴 코드

      • ESLint가 많이 도와줌

      • 가독성 증가

      • 재사용 가능한 컴포넌트 개발 지향

    • 백엔드는 데이터에만 집중하기

      • 서버 렌더링이 없으니 데이터만 생각하면 됨

      • API Gateway의 역할에 집중

3. Spring Data JPA의 사실과 오해

NHN Dooray개발실 - 신동민

  1. Spring JPA의 사실과 오해

  2. 연관 관계 맵핑에 대한 모든 것

  3. Spring Data JPA Repository의 숨겨진(?) 기능


요약

  • 연관관계 매핑

    • 사실상 단방향 매핑만으로 연관관계 매핑은 이미 완료

    • 대개의 경우 단방향 매핑이면 충분하다.

    • 하지만, 일대다 단방향 연관관계 매핑에서 영속성 전이(cascade)를 사용할 경우 양방향으로 변경하자

      • 추가 update 쿼리 방지

  • Spring Data JPA Repository

    • JpaRepository 상속하면 웬만한 CRUD, Paging, Sorting 메서드 사용가능

    • 메서드 이름 규칙을 통한 쿼리 생성 가능

      • 이름 규칙에 따라 interface에 메서드 선언만 하면 쿼리 생성

    • JPA Repository 메서드로도 JOIN 쿼리 수행 가능

      • 이름 규칙에 따라 Entity 내 연관관계 필드 탐색함

    • JPA Repository 메서드에서도 다양한 DTO

    • Projection 지원

      • Dynamic DTO Projection도 가능하다.

관련 예제

4. 바르게, 빠르게! Reactive를 품은 Spring Kafka

NHN 이병찬

  1. Kafka 메시지를 비동기로 처리하는 방법

  2. ReactiveX에서 제공하는 연산자를 활용하는 사례

  3. Project Reactor의 내부 구조(Publisher-Subscriber 간 처리 흐름)

예제 코드 저장소 - https://github.com/EleganceLESS/nhn-forward-2019


01. Kafka 그리고 Spring

  • 가장 인기 있고 대중적인 스트리밍 플랫폼

  • Spring에서 Kafka 쓰는 방법이 매우 간단해 졌다. JMS 리스너와 유사.

  • 제약 조건

    • 1스레드당 1개의 일감을 처리할 수 있는데, 일감이 오래걸리면 병목이 발생한다.

    • 병렬로 처리할 순 있으나 동일한 문제는 여전히 존재 한다.

  • 스트리밍 플랫폼의 본질?

    • producer의 publish

    • consumer의 subscribe

    • 그 사이의 stream

  • Kafka를 reactive하게 사용하기 위해 Reactor Kafka 또는 Spring Kafka 사용

    • producer가 publish 할 때, Flux를 만들어서 stream에 밀어 넣는다.

    • consumer도 subscribe 할 때, Flux로 가져와서 처리한다.

02. 적용 프로젝트 소개

  • 서버 모니터링 시스템에서 reactive kafka를 활용했다

  • 기본 로직은 서버에서 메트릭 정보를 가져와서 메트릭DB에 넣고 그것을 보여준다.

  • 그 사이에서 Detector(관찰자)가 존재하여 메트릭 정보를 활용해 사용자에게 Event를 전달한다.

  • 이 때 Detector는 수 많은 서버에서 오는 메트릭을 다 관장해야 한다. 커버링 범위가 매우 크다.

    • 1000 대 이상의 서버에서 동시다발적으로 이벤트가 감지되면?

    • 해당 이벤트가 짧은 간격으로 수차례 반복하면?

  • 어쨋든 이상 이벤트 통지에 지연이 발생해서는 안되며, 각 이벤트는 상호 독립성이 보장되어야 한다.

03. 기본 기능 구현

  • 이벤트가 발생하면 Detector는 그 정보를 메세지로 만들고 스트림에 집어 넣는다.

  • 이벤트 프로세서에서는 메세지를 읽고 DB에 기록 후, 필요하면 메세지 발송 요청을 보내자.

04. 메세지 중복 제거

  • 서버에서 Dector에게 데이터 보내고, 문제가 있으면 Event에 집어 넣는다.

  • 우리는 Detector가 매우 중요하므로 HA 구성을 하게 된다.

  • 2대의 Detector는 2개의 input과 2개의 ouput이 발생한다. 이벤트 중복을 제거하자.

    • Flux Operator의 sampleFirst(), groupBy()를 이용해서 처리

05. 데이터 모아서 처리하기

  • 최악의 상황의 경우? 동시다발적 또는 많은 양의 메세지 발생

  • 기준 시간 동안 발생한 여러 이벤트는 하나의 메세지로 모아서 통지하자.

    • 발생하는대로 메세지를 보내게 되면 1000건 발생하면 1000개의 메세지를 받게 된다.

    • ex) 30초 단위로 버퍼에 쌓아서 1건으로.

  • Flux Operator의 buffer() 이용해서 처리 가능하다.

public void process() {
 consume().flatMap(this::recordToNotifyObject)
  .groupBy(Message::key)
  .flatMap(flux -> flux.buffer(Duration.ofSeconds(30))) //버퍼링 - 30초
  .flatMap(this::notify)
  .flatMap(this::saveResult)
  .subscribe();
}

06. 정해진 양 만큼만 처리

  • 우리가 잘 만들었다고 쳐도, 이 시스템을 사용하는 연관 시스템이 해당 API Call을 견딜 수 있는지 확인해야 한다.

  • API 서버도 reactive하게 만들었다. 해결 됐지 않나?

    • 결국 그 요청은 DB가 다 받는다.

  • 발송 시스템의 경우에도 과도하게 요청을 다 보내면 과금이 어마어마하게 늘어난다.

  • Custom Subscriber를 만들어서 subscribe할 때, hookOnSubscribe() 를 정의할 수 있다. (정해진 양 만큼 발송)

07. 시간을 달리는 메시지

  • subscribe 시작시점에는 순차지만, 비동기기 때문에 끝나는 시점에 순서를 보장할 수 없다.

  • request(4)로 요청해서 1243 순서로 끝났는데 서버가 뻗었다.

  • 그 다음 요청은 3부터 시작해서 onNext() 4567을 처리하게 된다.

  • 해결 - offset이 증가하는 경우에만 commit을 하자.

    • 코드는 추후 제공되는 영상 참조

08. 몇 가지 결론

  • 많이 공부하고, 많이 고민하고, 적게 코딩하자







'Experience > 2019' 카테고리의 다른 글

우아한 Redis 세미나 후기  (0) 2019.11.22
AWS SUMMIT SEOUL 2019 2일차(04/18) 참관기  (0) 2019.04.19
댓글