티스토리 뷰
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년간 운영해보니...
프롤로그 : 무엇이 문제인가?
일체형 웹 서비스(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개발실 - 신동민
Spring JPA의 사실과 오해
연관 관계 맵핑에 대한 모든 것
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 이병찬
Kafka 메시지를 비동기로 처리하는 방법
ReactiveX에서 제공하는 연산자를 활용하는 사례
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 |
- Total
- Today
- Yesterday
- JPA
- 순환
- Raspberry Pi
- 레드블랙트리
- Recursion
- 한밭대학교
- 정렬
- 한밭이글스
- Java
- ORM
- 자바
- 알고리즘
- IT융합인력양성사업단
- 시간복잡도
- 인프런
- vuex
- github
- 무선통신소프트웨어연구실
- Vue.js
- vuejs
- Spring
- AWS
- 스프링부트
- 젠킨스
- Algorithm
- RBT
- Wisoft
- 라즈베리파이
- Spring Boot
- springboot
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |