f-lab-edu / hello-world Goto Github PK
View Code? Open in Web Editor NEW[렌딧 합격, 초봉 4500 이상] 언어교환 상대찾기 서비스
[렌딧 합격, 초봉 4500 이상] 언어교환 상대찾기 서비스
사용자가 각 친구에게 추천글을 남길 수 있도록 함으로써 다른 사용자가 언어 교환 파트너를 찾는데 있어 도움이 되도록 하기 위함
추천글 등록 시 알람까지 함께 보내는 로직이 필요
사용자가 추천글을 남기기 위해서 기준을 충족하는 지 검사하기 위한 친구기간 구하기 메소드 추가하기
위의 메소드를 포함하고 다른 예외 검증 기능들도 가진 추천글 남기기 기능 구현하기
성공 케이스에 대한 테스트 작성하기
실패 케이스에 대한 테스트 작성하기
불필요한 중복이나 코드를 지저분하게 만드는 if
문을 최대한 제거
리턴/본문 내용이 필요 없는 컨트롤러 메소드들의 ResponseEntity<Void>
를 리턴타입을 제거하고 @ResponseStatus
로 대체
논리삭제
를 이용모국어(NATIVE)
인 언어를 제외한 다른 언어들의 레벨을 변경할 수 있는 기능 구현하기(성공 케이스)
(실패 케이스)
Enum
을 도입 ~~RuntimeException
을 상속 받고 메소드를 오버라이딩 하는 등의 번거로운 작업 대신 상수만 추가해서 특정 예외관련 1개의 클래스만 가지고 통합적인 예외 처리를 하기 위한 것이 목적User
와 File
에 대한 예외관리 Enum 작성 UserController
에 있던 비즈니스 로직을 LoginService
로 이동기존
: UserController에서 로그인을 할 때 컨트롤러가 UserService에서 유저 객체를 받아와 LoginService로 전달
문제점
: 유저정보를 받아오는 부분은 비즈니스 로직
에 해당한다.
목표
: 유저정보를 받아오는 로직을 LoginService
로 이관해서 UserController -> LoginService -> UserService
의 의존관계를 형성하는 것
Scale Out
이 더 적합함데이터 불일치
가 발생한다는 문제가 있음세부적인 trade-off
을 고려해 세션 저장소를 추가하기로 했으며 Redis
가 가장 적합한 후보로 선정됨메신저 기능을 추가했으나, 실시간 처리가 중요한 로직 내부에 개별 메시지마다 DB로의 접근과 블로킹 IO가 발생한다는 문제점이 있었음
위의 문제점을 해결하기 위해서 다양한 방법 중 성능과 안정성이 검증된 메시지 큐인 카프카를 사용하기로 결정하였고, 이를 도입하여 DB 연결 및 블로킹 IO의 발생을 최소화 해서 대용량 트래픽에도 안정적으로 실시간 처리를 해낼 수 있도록 하는 것을 목표로 삼음
카프카에 대해 학습하기 (카프카, 데이터 플랫폼의 최강자)
Google Cloud Platform에 Zookeeper 지노드 및 카프카 브로커를 위한 VM서버를 추가하고 설치하기
여러 개의 서버에 지노드/브로커 클러스터링 구성하기
카프카 토픽 또한 클러스터링 구성에 대한 설정을 추가하여 안정성 높이기
프로젝트에 카프카 프로듀서/컨슈머 설정 추가하기
프로듀서/컨슈머를 통해 들어온 메시지를 모아서 카프카 서버로 보내고, 이를 다시 컨슈머를 활용해 배치형태로 불러와 DB에 한 번에 저장하는 구조 구현하기
Filter
vs Interceptor
vs AOP
각각에 대해 조사하기
각각의 특성
을 고려하여 최선의 결정 내리기
MyPageController와 관련 도메인 클래스들 코틀린으로 변환
깨진 테스트 코드들 고쳐주기
사용자의 개별 알람을 읽어올 수 있는 기능 구현하기
읽은 알람은 읽음 상태를 'Y'로 변경할 수 있는 기능 구현하기
성공 케이스에 대한 테스트 작성하기
실패 케이스에 대한 테스트 작성하기
사용자 존재유무
와 이메일 일치
확인UUID
를 이용해 사용자에게 전달해줄 임시 비밀번호를 생성EmailService
클래스 작성properties
파일 분리하기 실제 서비스 운영
환경을 위한 properties
파일 작성하기
개발 중에 사용할 로컬
환경을 위한 properties
파일 작성하기
테스트시 사용 될 테스트
환경을 위한 properties
파일 작성하기
필요한 설정/테스트 파일에 @Profile
, @ActiveProfile
적용하기
특정 사용자의 프로필을 조회할 때 DB의 부하를 줄이기 위해서 해당 사용자에게 등록된 추천글은 최신 것부터 일부만 추려서 로드합니다.
그래서 혹시나 프로필을 조회하는 사용자들이 나머지 추천글도 보고자 한다면 프로필 내부에 링크를 두어 새로운 URL로 연결해주어 전체 추천글 목록을 확인할 수 있도록 합니다.
추천 글을 가져오기 전 해당 사용자가 존재하는지 여부를 판별하는 코드 작성
cursor 방식 페이징을 활용해 해당 사용자에 대한 추천글을 불러오는 로직 및 쿼리 작성
위의 로직을 테스트 할 테스트 코드 작성
포스트맨을 활용해 전반적인 동작 테스트
성공 케이스 테스트 작성
실패 케이스 테스트 작성
사용자의 모국어를 추가할 수 있는 기능 구현하기
사용자가 학습할 수 있는를 추가할 수 있는 기능 구현하기
사용자가 학습 중인 언어의 레벨을 추가할 수 있는 기능 구현하기
(성공 케이스)
(실패 케이스)
성공 케이스 테스트 작성
실패 케이스 테스트 작성
(성공 케이스)
(실패 케이스)
사용자가 다른 사용자들과 주고 받은 메시지를 확인하기 위해서 이를 불러올 기능이 필요함
각 사용자와의 채팅내용을 보여주기 전에 각 채팅내용을 담고 있는 채팅함 목록을 먼저 보여줄 필요가 있음
메신저 앱과 같이 채팅함 이름으로는 상대방의 아이디를 사용한다.
해당 채팅함에는 자신 혹은 상대방 중에 누가 보냈는지에 상관 없이 가장 최신의 메시지를 1개 뽑아서 보여준다.
표시한 메시지는 보낸 시간을 표시한다.
채팅함 목록은 가장 최신 메시지를 받은 채팅함 부터 표시한다.
Controller, Service, Mapper의 각 계층에 필요한 메소드 작성하기
요구사항을 만족하는 쿼리를 실행계획을 고려하여 작성하기
Java 언어의 한계점
을 인지하고 이를 손쉽게 보완하는데 초점을 맞추고 있는 Kotlin으로 전환
Java와 Kotlin은 JVM을 기반으로 하여 상호 간의 호환이 잘 되므로 부분씩 전환하기가 용이
UserController와 관련 도메인 클래스들 코틀린으로 변환
깨진 테스트 코드들 고쳐주기
Hello-World
서비스에서는 추천글 사용의 오남용을 막기위해 단 1회만 추천글 작성을 허용하며, 작성한 추천글은 삭제하거나 수정할 수 없습니다.
다만 오타나 내용 추가 등의
의 이유로 수정이 필요할 경우 48시간 동안 수정을 허용합니다.
Hello-World 서비스를 사용하는 사용자들이 등록된 다른 사용자들의 프로필 사진 및 정보를 살펴보면서, 더 알아보고 싶은 사용자가 있는 경우 클릭하여 개별 사용자들의 프로필을 조회할 수 있도록 해주는 페이지를 구현합니다.
특정한 커서가 지정되어 있지 않은 첫 페이지(메인 페이지)의 경우에는 캐싱 기능을 활용해 캐시 저장소에 미리 저장해두어 데이터를 로드하도록 설정해줌으로써 서버로 가해지는 부하를 줄일 수 있도록 하는 것이 목표입니다.
프로필 조회 기능
구현하기(이때, 해당 기능의 경우는 개인 프로필 조회와 다르게 간단한 정보만을 가지고 프로필을 리스트 형식으로 나열
하는 것이 목표이므로 필요한 정보만 가져온다.)
(ex. 아이디, 프로필 사진, 추천글 갯수 등)
성공 케이스 테스트 작성
실패 케이스 테스트 작성
서버 측에서 클라이언트로부터 WebSocket 연결 요청을 받아 처리할 수 있도록 하는 설정 추가하기
메시지 전송 요청을 매핑하여 해당 메시지는 DB에 저장하고, 대상 사용자에게는 알림을 보낼 수 있도록 하는 기능 구현하기
위의 로직을 테스트 할 단위 테스트 작성하기
users 테이블의 구조를 변경해서 국가/도시 정보의 경우는 정수 값만 저장
하고 이름을 가져올 때는 외부 테이블을 매핑
하도록 변경
DB에 저장된 정보를 Join을 통해 결합해서 가져온 뒤 비즈니스 계층에서 추가 로직 처리를 통해 해당 사용자의 프로필을 구성하는 객체를 만들 수 있는 기능 구현
프로필 정보에 담을 추천 글을 가져올 수 있는 기능 구현
불필요한 DB 커넥션이나 조인 비용을 줄이기 위해서 거의 변하지 않는 정보들을 캐싱
한 뒤 캐싱된 맵을 이용해서 국가/도시/언어 이름을 가져오도록 만들기
사용자가 지금까지 받은 알람 목록을 한꺼번에 확인할 수 있도록 받은 알람 목록을 불러오는 기능 구현이 필요
페이징을 사용해서 한번에 모두 불러오는 것이 아니라 각 페이지 만큼 요청을 나눠서 처리하는 것이 목표
사용자가 받은 알람을 선택적으로 삭제할 수 있도록 받은 알람 삭제 기능을 추가하는 작업이 필요
친구 요청 등 철회 시 보냈던 요청 삭제와 함께 발송했던 알람 내역까지 삭제하는 작업이 필요
사용자가 선택적으로 알람을 삭제할 수 있도록 하는 기능 구현하기
요청 철회 시 보냈던 알람도 삭제할 수 있도록 해주는 기능 구현하기
성공 케이스에 대한 테스트 작성하기
실패 케이스에 대한 테스트 작성하기
기존
: UserController에서 로그인을 할 때 Controller에서 유저 객체를 받아와 UserService로 전달하는 로직
문제점
: 유저정보를 받아오는 부분은 비즈니스 로직
에 해당한다.
목표
: 유저정보를 받아오는 로직을 LoginService
로 이관해서 UserController -> LoginService -> UserService
의 의존관계를 형성하는 것
현재 비밀번호가 일치하지 않으면
예외를 발생시킵니다.새로 입력한 비밀번호
가 현재 비밀번호
와 일치
하면 예외를 발생시킵니다.새로 입력한 비밀번호
와 입력값을 다시 확인하기 위한 값
이 불일치
하면 예외를 발생시킵니다.DAO
를 통해 DB에서 현재 회원의 비밀번호를 업데이트 합니다.offset
방식이었습니다.페이지 요청사이에 데이터 변화
가 있는 경우 중복 데이터가 발생
하거나 데이터의 생략
현상이 발생할 수 있습니다.
offset 방식을 사용하면 기본적으로 위치를 찾기 위해 앞에서 읽었던 행을 다시 읽어야 하므로
저장된 데이터가 많아질수록 성능적인 이슈가 발생합니다.
cursor
방식으로 변경하기로 결정하였습니다.Hello-World 서비스는 대용량 트래픽이 들어오는 것을 가정하고 프로젝트를 진행하므로, 그에 맞게 데이터의 수도 당연히 많을 것입니다.
cursor 방식을 사용하면 읽은 데이터의 바로 다음부터 limit 까지만
데이터를 불러오면 되고, 이때 기준을 인덱스를 PK와 같은 인덱스를 사용한 칼럼으로 지정한다면 인덱스를 100% 활용할 수 있어 성능이 더 뛰어납니다.
페이스북, 인스타그램 등의 다양한 SNS 서비스들 또한 이러한 이유로 cursor
방식을 활용해 페이징을 구현했습니다.
친구 추가/수락, 추천글 남김 등의 행동이 발생하면 대상 사용자가 이를 확인할 수 있도록 촉진하는 매개체가 필요
Push 알림을 통해 이러한 부분을 해결 가능
Firebase Cloud Messaging 설정 추가
클라이언트가 FCM 서버로부터 전달 받은 토큰을 서버에 보관할 수 있도록 로그인 메소드 수정 및 DB 설계
토큰을 저장, 갱신하고 얻어올 수 있는 기능 구현하기
등록된 토큰을 통해 토큰을 등록한 사용자에게 푸시 알림을 전송할 수 있도록 FCM 서버에 요청하는 기능 구현하기
현재 정보를 변경
하는 기능 구현 (변경 불가능 한 정보 제외 ex. ID, Password, Email 등)프로필 사진을 추가/변경
하는 기능 구현파일
을 저장소에 저장
하는 기능 구현.유저의 ID를 반영
한 디렉토리 생성UUID
를 활용해 랜덤한 숫자
형태로 파일이름 변경
FileNotUploadedException
저장소의 용량 낭비를 막기 위해
기존 프로필 사진 파일이 있다면 지우고 새로 요청 받은 파일을 저장하는 기능 구현FileNotDeletedException
UserService와 관련 도메인 클래스들 코틀린으로 변환
깨진 테스트 코드들 고쳐주기
성공 케이스 테스트 작성
실패 케이스 테스트 작성
(성공 케이스)
(실패 케이스)
CI란 지속적 통합(Continuous Integration)
의 줄임말로써 빌드 -> 테스트 -> 통합
으로 이어지는 과정을 자동화
함으로써 개발자들이 더 빠른 주기
로 작업/업데이트한 내용을 통합 Branch에 통합
하고 빌드
하는 개발 방식을 의미한다.
CI가 등장하기 전에는 위의 지난한 과정을 모두 수동
으로 일일이 거쳐야 했다.
CI를 도입하면 이러한 단계를 자동화하여 생산성을 증대
시킬 뿐만 아니라 작업 내용을 통합하기 전에 빌드 테스트가 먼저 실시되어 내용을 검증
하므로 통합된 코드에 대한 안정성
과 신뢰성
이 올라가게 된다.
CI를 적용하기 전 CI
와 CI의 발전과도 연관이 있는 agile 방법론
에 대해서 학습하기
Azure 클라우드를 통해 Jenkins
설치하기
Jenkins
와 GitHub
를 Webhook
을 통해 연결하여 PR
시 Jenkins가 해당 사실을 인지할 수 있도록 설정하기
CI 파이프라인
을 구축해서 Poll -> Build -> Test
과정의 자동화를 실현하기
Jasypt 라이브러리 및 bean configuration 추가
민감한 설정값들 암호화 하기
테스트 구동
성공 케이스 테스트 작성
실패 케이스 테스트 작성
많은 부하
가 가해지는 연산이다JOIN 연산
이 활용된다.)쿼리 튜닝
및 로컬 캐시
를 이용해 국가/도시/언어 이름은 애플리케이션 단에서 매칭시키도록 구조를 변경 해 DB에 가해지는 부하를 어느 정도 감소 시켰으나 여전히 JOIN연산는 DB에 부담이다.해당 사용자에 대한 추천글도 프로필에 담기 위해서 DB로의 연결이 한 번 더 필요하다.
이후 애플리케이션 단에서 국가/도시/언어 이름 매칭 작업
및 위의 정보를 활용해 유저 프로필 정보를 담은 객체 생성
작업이 실행된다.
유저 프로필 정보는 자주 변경될 확률이 낮다.
이러한 근거를 바탕으로 캐싱을 적용해주는 것이 성능 향상에 큰 도움이 된다는 결론에 이르게 되었다.
로컬 캐싱
vs 글로벌 캐싱
각각에 대해 조사하기
trade-off
를 고려하여 현재 서비스에 맞는 결정 내리기
Redis
Redis를 활용한 캐시 설정파일 추가하기
사용자 프로필 조회 시 사용자의 아이디를 key 값으로 한 캐시 값을 Redis 저장소에 저장하고, 일치하는 캐시가 있으면 이를 불러오도록 설정
사용자 프로필에 반영되는 정보가 수정될 경우 이를 반영하기 위해 캐시가 Evict 되어 최신 정보를 반영할 수 있도록 설정
사용자 프로필 찾기 기능
구현하기(이때, 전체 사용자들 프로필 조회에서 사용했던 부분을 재사용할 수 있다.)
Validator
내용에 대한 단위 테스트 작성하기
조건이 여러개가 부여되는 만큼 직접 통합테스트를 작성해서 동작 여부 확인하기
포스트맨을 활용해 전반적인 동작 테스트
성공 케이스 테스트 작성
실패 케이스 테스트 작성
LoginService와 관련 도메인 클래스들 코틀린으로 변환
깨진 테스트 코드들 고쳐주기
사용자가 다른 사용자들과 주고 받은 메시지를 확인하기 위해서 언제든지 이를 불러올 수 있어야 함
각각의 채팅함 내부에 저장되어 있는 메시지를 불러오는 기능 필요
채팅함 id를 전달 받으면, 해당 채팅함에 있는 메시지를 불러온다.
모든 메시지를 한 번에 불러오는 것은 데이터가 많아질수록 엄청난 부하를 가하게 되므로, 커서 방식으로 적절히 나눈다.
기존에 읽지 않은 메시지가 있을 경우 읽지 않음을 읽음으로 표시
한다.
Controller, Service, Mapper의 각 계층에 필요한 메소드 작성하기
요구사항을 만족하는 쿼리를 실행계획을 고려하여 작성하기
Master-Slave
구조를 설정하여 CRUD의 CUD
를 주로 Master 서버에서 처리하고, 이를 Slave로 동기화
하는 방식으로 만들기
(데이터의 정합성이 중요한 읽기 데이터의 경우는 마스터에서 바로 읽을 수 있도록 해보기)
Slave
에서는 CRUD의 R
을 주로 담당하는 구조로 만들기
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.