데브코스 백엔드 3기 85일차
월화수목금! ㅋㅋㅋ 요일이 점점 길어지지만,, 기록에 의미를!
애자일에 대해 배웠다!
평소에 애자일하게 살고 있었더군여
일기(회고)
- 지난주의 나태함을 보완하고자 분주하게 움직이겠다고 한 다짐을 지켜 뿌듯하다
- 강의 안밀렸고! CS공부하고, 프로젝트 도메인 정했고,
사용해보고 싶은 기술에 대해 일단 개념을 공부했다
1일 1영상, 1알고리즘도 잘 실천했다 칭찬하기!
(알고리즘 늘고 있는거겠지..? 요즘 나의 최대 고민 알고리즘)
- 강의 안밀렸고! CS공부하고, 프로젝트 도메인 정했고,
- 이번주 CS공부는 web 인증방식과 캐시에 대해 공부해보았다
- 인증방식은 데브코스 강의를 통해 배웠지만
cookie, session, jwt의 각각의 특징과
그로 인한 차이점들에 대해 정리해보고싶었다 - 캐시같은 경우엔, 이번 프로젝트는 써보고싶은 기술 1가지 이상은 꼭 써보자! 라는 생각에 캐시를 떠올렸고
사용을 위해 개념부터 공부를 시작했다
그래서 이번 프로젝트에서는 로컬캐시인 caffeine cache를 사용해볼 예정이다
- 인증방식은 데브코스 강의를 통해 배웠지만
- 이번 프로젝트를 통해 얻고싶은 것들에 대해 생각해볼 것!
- 사용하고 싶은 기술을 고려해볼 것
- 지난번 프로젝트는 내가 맡은 기능이 너무너무 어려워서(애증의 커스터마이징)
사실 기능 돌아가게 하는데에만 온 정성을 쏟아부었다(돌아갈 때 엄청나게 뿌듯햇쥐)
간신히 만든 느낌이라 써보고 싶은 기술이나, 코드 예쁘게 짜기 등은 전혀 고려하지 않았다
리팩토링에서 코드는 다듬었지만 기술 추가까지는 하지 않았기 때문에
이번에는 기술까지 고려해보기!
- 지난번 프로젝트는 내가 맡은 기능이 너무너무 어려워서(애증의 커스터마이징)
- 기술적 어려움을 문서화 해둘 것
- 미래의 나도 알 수 있고, 프로젝트의 용도가 포트폴리오니까
나의 노력과정을 면접관도 알 수 있고
가장 가까운 이유로는 현재 팀원들도 알 수 있게 공유가 된다
서로 질문이 줄어드니 생산성이 좋아지고, 나아가서 프로젝트의 수준이 높아질 수 있다고 생각한다
- 미래의 나도 알 수 있고, 프로젝트의 용도가 포트폴리오니까
- 사용하고 싶은 기술을 고려해볼 것
- 내가 이번 프로젝트에서 사용해보고 싶은 기술들은
caffeine cache, query dsl이고
파일업로드를 해보고 싶다
그래서 상품파트를 맡고싶었고 그렇게 되었다(다행히 인기가 없었다)
의도치 않은 복병이 생겼지만..!(최저가, 최고가ㅎㅎ)- 일단, 파일업로드 + 캐시가 되면 서버가 터질것 같다고
훈님이 조언해주셔서 S3가 추가되었다
짧은 기간에 사용하고싶은 기술이 많아서
우선순위를 생각해보아야겠다 - 구현하고 싶은 기능보다 기술에 초점이 맞추어지고 있는데
내가 의도한 바는 맞지만, 현재 이 방향이 올바른것인가
올바르지 않더라도 이번엔 그러고 싶어!
- 일단, 파일업로드 + 캐시가 되면 서버가 터질것 같다고
애자일 방법론(Agile Methodology)
폭포수 모델 방식
으로 일하면(사진에서 위)- 변화된 트랜드 반영 불가
- 진행상황보고, 성과 압박
- 수익발생 없이 계속되는 비용지출
애자일 방식
으로 일하면 위의 단점들의 반대가 장점이 됨
스크럼에서의 역할
1. 애자일 코치/스크럼마스터
팀보호를 위한
- 스크럼의 가치를 잊지않게 해주는 사람
- 보다 더 효율적이고 효과적으로 일하는 방법을 고민하는 사람
- ex) 미팅 언제할지
- 별명은 헌신적인 리더
2. 프로덕트 오너
팀 발전을 위해서 무조건 시키는 느낌
- 이 서비스에 필요한 개선사항과 요구하는 사항을 정리하고 전달
- ex) 그냥 어렵다!를 들으면 정확히 뭐가 어려운지 찾아내야
- 방향을 정하고 결정을 내리는 사람
- 끊임없이 배우고 또 배우는 사람
3. 개발자들
문제해결점을 찾는
- 스스로 주체적으로 일하는 사람들
- 교차기능적으로 협업하는 T자형 인재
- 기술적 채무를 만들지 않는 사람
- ex) 코드 지저분하게 쓰는 사람, 나중에 고치겠다는 채무
스크럼
1. 비전
3-5년짜리 목표
- 세우는 이유 : 통제가 아닌 맥락으로 리드
- 어떻게 세울까? : 엘리베이터 법칙
2. 로드맵
1년짜리 목표 -> 분기별로 2-3개씩 작성
3. 프로덕트 백로그
제품에 대한 할일 목록, 3개월짜리 할일 목록
- 우성순위에 따라 정렬되어 있어야 함
- 프로덕트 오너가 관리(무슨일 해해야 하는지 파악 후 우선순위 정리)
- 할일 목록을 유저스토리 형태로 작성
- (사용자)는 (목적/목표)를 위해 (필요)를 원한다
- 작성이유
- 나도 모르는 사이 고객의 입장에서 생각하게 되어버림
- 원래 하려던 방식말고, 더 좋은 방식으로 일을 하게됨
- 복잡한 기획안을 쓸 필요가 없어짐(실제 대화를 통해 소통)
- 유저스토리 워크샵 : PO가 비전, 로드맵 설명 -> 각각 사람들 유저스토리 적음 -> 정리(같은거 합치고) -> 이걸로 스토리보드 만들기
- 플래닝포커 : 스토리포인트(데드라인이 아닌 그저 상대적 측정) - 상대적인 작업시간
- 타임박스 : 시간을 끊어서 박스포장 하듯이 -> 이 시간만 써서 일을 처리하겠다는 의미
4. 릴리즈 플래닝
5. 스프린트
1-4주 단위(2주 추천)
- 첫째날 : 스프린트플래닝 -> 스프린트 백로그가 나옴
- 2~종료 전날 : 데일리 스크럼
- 그 중 최소 한번은 프로덕트 백로그 다듬기 미팅(PO가 다듬는, 아이템 나올 때마다 다들고 + 중간중간 같이 다듬고)
- 마지막날 : 리뷰, 회고
- 모든 업무는 스프린트 내에서 시작하고 종료
- 매 스프린트는 이전의 스프린트보다 나아져야
- 스프린트 중간에는 새로운 일이 추가되면 안됨
- 애자일 스크럼, 스프린트 다 기간 끊어서 진행하는 이유 : 한번 해보고 이를 통해 배움을 얻어서 다음번에 나아지기 위해서
- 스프린트 플래닝(스크럼 마스터가 진행)
- 스프린트 백로그
- 프로덕트 백로그 중 이번 스프린트에 해야하는 일들만 넣음
- 이번 스프린트 동안 해야하는, 유저스토리에 적혀있는 일들
- PO가 이번 스프린트에 해야하는 일들을 설명
-> 개발자들은 해야하는 유저스토리를 작은 여러개의 할일로 쪼갬
-> 각각 세부항목의 상대적인 작업시간
-> 스프린트 목표정하기(PO가 작성) : 스프린트 백로그를 아우르는 한문장, 스프린트 끝난 후 어떤걸 달성했으면 좋겠는가
- 스프린트 백로그
- 프로덕트 백로그의 주인 : PO
- 스프린트 백로그의 주인 : 개발자들
6. 데일리 스크럼
- 첫날(백로그하니까), 마지막날(리뷰, 회고 하니까) 안해도됨
- 스프린트 달성을 위해
- 무얼 했는지
- 오늘은 무얼할건지
- 직면한 문제는 무엇인지
- 프로덕트 백로그 다듬기(걱정되는 지점들, 추가할 백로그 등)
7. 리뷰, 회고
뒤를 돌아보지 않으면 발전이 없다
- 스프린트는 무조건 전보다 나아져야함, 발전이 없다면 제대로 하고 있는게 아님 그러기 위해서 리뷰,회고
- 리뷰 : 결과물을 되돌아봄
- 스프린트 결과물 / 진행중 발생했던 상황 소개
-> 결과물에 대한 피드백
-> 다음 스프린트 계획 + 프로덕트 백로그 다듬기
- 스프린트 결과물 / 진행중 발생했던 상황 소개
- 회고 : 허심탄회
- KPT(Keep Problem Try), PMI(Plus Minus Interesting), 4Ls 등의 여러 방식
- 이번주에 나에게 무슨일이 있었는지
- 어떻게 대처했는지, 어떤 기분이었는지, 어떤점이 아쉬운지
칸반가시화
- Kanban : 마켓매대
- 매대에 일정한 상품만 꺼내두고 비면 채우는 것처럼 일정 관리
이번주 영상
1/3 화, 우아한 테크토크 - 선착순 이벤트 서버 생존기 47만 RPM에서 살아남다
배달의 민족 실제 선착순 이벤트에서의 사례로 설명해줘서 흥미진진했다
선착순 치킨 이벤트
였는데,
이벤트 시 평소 주말 최고 트래픽(3000)대비 10배 이상의 순간트래픽을 예상했다고 한다
그런데, 실제로는 거의150배인 47만 RPM
이었다고..! 헉- 그래서 AWS scale-out or scale-up 해도
- 10배 예상했는데 150배 들어오면 또 장애
- PG사에서 먼저 장애가 나기도 함
- 일반 주문은 이벤트 영향 없도록 장애 격리하고 싶음
- 순간적인 대량트래픽을 소화하고 싶음
- 그래서 AWS scale-out or scale-up 해도
=> 큐시스템 사용!
큐시스템 + 프로모션용 서버, 스케줄러 만듬 + Redis 붙임
- 이벤트 주문 요청
-> 대기번호표 발급
-> 발급받은 사람 줄세움(대기열 - Sorted Set 사용)
-> 대기열 사용자에게 대기순서 몇번째인지 알려줌
-> 서버입장은 서버가 소화할 수 있을 만큼의 사용자만 입장(참가열)- 정리 : 일정한 수만큼 대기열에서 참가열로 이동시킴
위의 과정으로 Flow
- 이벤트 주문 요청
-> 대기번호 생성 + 대기열에 번호 추가
-> polling 하면서 참가열 진입 대기 + 몇번째인지 계속 알려줌 + 스케줄러는 1초마다 설정된 값만큼 대기열에서 참가열로 이동시킴
-> 대기번호가 참가열로 들어가면 기존주문 플로우를 탐-
정리 : 설정해 둔 일정한 트래픽만 들여보내줌
=> 순간적인 대량 트래픽 소화가능!
-
이 큐시스템을 어떻게 끼워넣지? + 일반 주문은 이벤트 영향 없도록 장애 격리하고 싶어!
=> 주문라우터 사용
주문라우터
:주문 시스템
으로 들어오는 모든요청의 출입구
RULE에 따라 API host
를 바꿈- RULE : 회원번호, 업소번호, 지역코드로 설정
- WebFlux + Netty 기반으로 만들어 -> 10000 TPS까지 처리 가능
=> 이벤트 참가한 사람만 큐 태우고 싶은데 아주 적절!
회원번호 이용하면 되니까
쿠폰발급
시마다 회원번호들을RULE에 있는 회원리스트에
넣어줌- RULE에 따라 API host를 바꿈
- RULE에 없는 요청 ->
기존 서버
타게됨 - RULE에 있는 요청 ->
큐시스템
타게됨
- RULE에 없는 요청 ->
- 선착순 flow와 대용량 트래픽관리에 대해 실제 상황과 해결방안을 보니 재미있고 멋있었다
Redis는 낯선 개념이었는데 공부해보고 싶어졌고, 라우터 서버 생각하고 구현한분, 너무 멋있다!
이번 프로젝트, 선착순에 조금 욕심이 났다ㅋㅋ
이번엔 이미 사용하고 싶은 기술을 정했으니, 다음번을 노려보아야지
1/4 수, 백기선님 - 자바, 생성자 대신 정적 팩토리
정적팩토리
(추상 팩토리패턴, 팩토리 메서드 패턴과 전혀 무관)
1. 생성자 대신 정적팩토리 메서드를 고려해라 (생성자 시그니처가 중복되는 경우에)
- 동일한 시그니처 생성자 → 컴파일 에러
- 생성자 - class 이름과 동일하게 만들어야하고 / class type 만 리턴
⇒ 정적팩토리 메서드를 사용해보자
정적팩토리 메서드
: 단지인스턴스를 만들어 리턴
하는 팩토리- 만들어주는 객체의
특징
을팩토리메서드 이름
만으로도 잘 표현가능
- 만들어주는 객체의
2. 호출될 때마다 새로운 인스턴스를 만들고 싶지 않을 때
- 생성자 통해서 인스턴스 생성 → 통제 불가능
- 어디서든 이 생성자 호출해서 인스턴스 생성 가능
- 생성자로는 못하는일(인스턴스 생성컨트롤 하는일)을 정적팩토리메서드로는 가능(팩토리메서드 내부에서 자기자신이 컨트롤)
⇒ 인스턴스 하나만 존재해야 한다면 → 기본생성자 private static final + 정적팩토리메서드
를 사용해보자
3. 매개변수에 따라 다른 인스턴스를 리턴할 수 있음
- ex) Boolean.valueOf(boolean b)
- 매개변수 b가 true 이면 미리 만들어둔 상수인 TRUE 리턴
private static final Boolean TRUE = new Boolean(true);
- 매개변수 b가 false 이면 미리 만들어둔 상수인 FALSE 리턴
- 매개변수 b가 true 이면 미리 만들어둔 상수인 TRUE 리턴
- 참고) 디자인패턴의
플라이웨이트패턴
: 자주 쓰는 인스턴스들을 만들어놓고 미리 캐싱해두고 꺼내서 사용
4. 인터페이스 기반의 framework를 사용할 수 있게 해줌
- 리턴타입은 인터페이스 or 상위 클래스(상위)
- 실제 반환하는건 구현체(하위)
⇒ 유연함 / 구체적인 타입을 클라이언트에게 숨길 수 있음
5. 정적팩토리를 작성하는 시점에 인터페이스만 있고 구현체가 없어도 됨
- 서비스 제공자 프레임워크(Service Provider Framework)
- Service Provider Framework 의 자바 기본 구현체인,
자바가 기본으로 제공해주는 정적 팩토리 메서드 :
ServiceLoader
- Service Provider Framework 의 자바 기본 구현체인,
자바가 기본으로 제공해주는 정적 팩토리 메서드 :
ServiceLoader<A> loader = ServiceLoader.load(A.class);
//ServiceLoader : iterator니까 -> loader에는 classpath내의 모든 A 구현체 담김
Optional<A> a = loader.findFirst();