-
Notifications
You must be signed in to change notification settings - Fork 1
5주차 그룹 리뷰 & 발표 준비
- 매도 API 적용
- 실시간 적용 (마이페이지, 주식 디테일)
- 즐겨찾기 레이아웃 구현 및 API 적용
- 내 정보 수정 레이아웃 구현 및 API 적용
- 헤더 최적화.
- 랭킹 API 연동.
- 뉴스 레이아웃 생성.
- 뉴스 API 연동.
- 검색 관련 오류 수정.
- 소켓 연결 관련 트러블 슈팅.
- 구독 해지 관련 문제 해결.
- 차트 고도화.
- 마우스 위치에 따른 값 표시.
- 마우스 이동 관련 최적화.
- 뉴스 api
- 네이버 뉴스 api를 통해 정보를 가져옴
- 소켓 통합
- 세션 두개로 하던 로직 하나로 합침
- 닉네임 관련 로직
- 회원가입 시
익명의 투자자 + PK 값
형태로 기본 닉네임 생성되게 했음 - 닉네임 변경 시
가-힣a-zA-Z0-9
정규식에 해당하는 것만 사용해서 닉네임 변경할 수 있게 했음 (특수문자, 한자, 공백 등 이런건 안되게)
- 회원가입 시
- FE - BE 소켓 테스트에 사용할 목데이터 생성
- locust 부하 테스트 스크립트 작성 및 테스트
- X-Forwarded-For → 다 한 ip에서만 요청이 가게 되니까 제대로 된 테스트가 안됐음. ip 매핑해주려고 사용
- 로그인 하고, 매수 해보고, 랭킹 요청 해보고… 하면서 부하 테스트를 해봄
- 카카오 oAuth, ranking API 수정
- nginx를 이용한 로드 밸런싱 구현
- 수평적 스케일링
- 종목 즐겨찾기 API 구현
- detail 페이지 조회할 때도 로그인 상태 + 즐겨찾기한 종목이면 true 반환할 수 있게
- 소켓 통합
- 주문 시 체결이 여러번 되는 문제 해결
- 원래 트랜잭션 처리가 제대로 안되고 있었음
- 트랜잭션 lock을 걸어줘서 잘 되고 있는듯함 (find 하고 있으면 다른 곳에서 읽기, 쓰기 다 안되게)
Track 2 - 4번 발표
🎙️ 이번 주 데모를 준비하면서 아래 내용을 고민해 보세요.
- 우리 팀의 키포인트가 되는 핵심 경험은 무엇일까요?
- 비슷한 주제나 기술을 사용하더라도 그 안에서 경험과 디테일에서 차이를 내는 부분이 무엇인지 정리해 보세요.
- 5주간의 경험을 정리하다 보면 자연스럽게 리팩토링을 포함해 완성도를 높일 수 있는 지점을 알 수 있습니다.
- 이를 통해 6주차의 계획도 같이 발표에 담아보고, 피드백을 받을 수 있겠습니다.
- 최종 발표든 GitHub이든, 누구나 납득 가능한 수준으로 프로젝트의 맥락이 설명되어야 합니다. 5주차 데모를 준비하면서 방법을 같이 고민해 보세요.
- 최종 발표는 PPT 자료로 진행됩니다. 아직 PPT로 준비한 경험이 없는 팀은 이번 기회에 연습해 보세요.
- 서비스 소개 (2분)
-
팀원 소개
저희 팀원들은 프론트엔드를 담당하신 서산님, 고동우님과 백엔드를 담당한 김진님, 이시은님, 그리고 저까지 총 다섯명으로 로 구성되어있습니다.
-
서비스 목적
우선 저희 서비스의 목적은, 실시간 주식 데이터를 활용한 모의투자 경험을 통해 주식 초보자들이 투자에 대해 배우고 익숙해지도록 하는 것입니다.
-
서비스 핵심 기능
서비스 핵심 기능은 크게 2가지로 주식 투자 요소와 랭킹 요소입니다.
- 실제 주식 투자 요소
- JuGa 서비스는 실제 주식 데이터를 실시간으로 제공하며, 사용자가 천만원의 가상 시드머니로 주식을 매수/매도할 수 있는 환경을 제공합니다. 실제 투자와 유사한 인터페이스를 통해 사용자들이 자연스럽게 주식 거래를 익힐 수 있습니다.
- 랭킹 요소
- 주식 초보 투자자들이 주식에 대해 조금이라도 더 친숙하게 다가갈 수 있도록 랭킹 시스템를 도입했습니다. 랭킹 탭에서는 하루 동안의 수익률 순위와 자산 순위를 확인할 수 있으며, 매일 갱신되는 랭킹을 통해 지속적으로 도전할 수 있는 재미를 제공합니다.
- 실제 주식 투자 요소
-
(1분28초)
-
기술적인 내용들 … (7분)
-
소켓 연결 제한 이슈
-
저희가 프로젝트 도중에 마주한 첫 번째 문제는 한국 투자 API의 소켓 제한이었습니다. 하나의 앱 키와 시크릿 키 조합으로 이루어진 세션 당 단 41개의 소켓만 사용할 수 있다는 제한이었습니다.
-
저희는 해당 문제를 해결하기 위해 크게 세 가지 방향으로 접근 해보았습니다.
-
첫 번째로 로드 밸런싱으로 세션 3개로 늘리기 위한 시도를 했습니다.
저희는 Nginx를 이용하여 로드 밸런싱을 구현했습니다. 총 3개의 서버를 구성했고 각각의 서버마다 다른 앱키와 시크릿 키를 할당하였습니다. 각각의 서버에 들어오는 요청은 Nginx가 ip_hash 방식으로 분배합니다. 한번 연결이 성립되면 계속 유지한 상태에서 데이터를 주고 받아야 하는 websocket의 특성 상 클라이언트의 요청이 분산되면 이전에 맺었던 요청이 끊어져 버리는 현상이 발생하였습니다. 이 때문에 동일한 사용자의 요청을 항상 동일한 서버로 라우팅 해야 하는 sticky session이 필요하여 Nginx의 ip_hash 방식을 사용하였습니다.
소켓 연결이 끊어지지 않고 유지하게 하면서 사용자의 요청을 분산하였습니다.
-
두 번째로 실시간 데이터가 가장 필요한 부분을 선별하고, 이외의 즐겨 찾기 종목의 수익률이나, 가격 조회, 상/하위 5개 종목 조회 등 중요도가 떨어지는 부분들에서는 소켓보다는 API나 SSE를 이용할 수 있도록 하였습니다.
-
세 번째로 사용하지 않는 소켓은 연결을 해제 하도록 하여 소켓의 낭비를 줄이기 위해 노력하였습니다.
- 그 과정에서 한 세션에서 여러 개의 이벤트를 관리를 하게 되었기 때문에, 같은 종목에 대해서는 모든 연결이 끊겼을 경우에만 구독이 끊기게 하여 잘못된 연결 끊김이 발생하지 않게 했습니다.
-
(3분 50초)
-
Redis를 이용한 성능 개선
- 서비스의 랭킹 기능과 최근 검색 기록 관리 와 같은 기능들을 MySQL로 구현하였을 때 몇가지 문제들이 예상되었습니다. 먼저 랭킹 테이블의 경우 데이터의 정렬과 집계를 위한 OrderBy 연산이 빈번하게 발생하였고 동시 접속자가 늘어남에 따라 부하가 증가하여 응답 시간이 지연될 것이라고 생각되었으며 검색 기록 테이블의 경우 사용자별 최근 검색어 조회를 위한 빈번한 I/O의 발생 과 리소스 낭비가 예상되었습니다.
- 이러한 문제들을 해결하기 위해서 Redis를 도입하기로 하였습니다.
- 저희가 Redis를 도입하기로 생각한 이유는 다음과 같습니다.
- 먼저 In-Memory 데이터 처리로 빠른 응답이 가능합니다. 또한 Sorted Set을 이용해 효율적으로 랭킹 관리가 가능합니다.
- 저희가 Redis를 도입하기로 생각한 이유는 다음과 같습니다.
(5분)
-
체결 동시성 트랜잭션 처리 → 체결 가능한 주문을 찾을 때 pessimistic_write 락을 걸어서 해결 (내일 테스트 해봐야 해결됐는지 알수있음)
- 로드밸런싱을 통해 서버를 여러 개로 늘리면서, 주문 체결 로직에서 동시성 문제가 발생했습니다. 예를 들어 10주 매수 주문을 넣으면, 각각의 서버에서 체결이 일어나 서버 개수인 3이 곱해진 30주가 체결이 되는 현상이 일어났습니다.
- 해당 문제를 해결하기 위해, 트랜잭션 로직을 수정했습니다. 체결 가능한 주식을 찾을 때 사용하는 기존 select 문을 select for update 문으로 변경하여 pessimistic_write 락을 걸도록 수정해 동시에 같은 주문에 접근할 수 없도록 구현했습니다. 페시미스틱 라이트?
(6분 15초)
-
SSE & Socket 비교 과정.
- 현재 저희 서비스에서는 양방향 통신이 필요하지 않습니다. 그렇기에 단방향 통신이 되는 SSE가 상당히 매력적으로 다가왔었는데요. 그럼에도 저희 서비스에서 SSE를 사용하지 않고 socket을 사용한 이유는 다음과 같습니다.
- 우선 HTTP/2를 사용하지 않을 때, 연결 최대 개수 제한으로 인한 한계가 존재했습니다. 크롬 브라우저 기준으로는 6개부터 제대로 동작하지 않았습니다. HTTP/2를 사용할 때는 기본값으로 100개까지 지원하나 nginx에 추가 설정이 필요하고, 여러 개의 요청이 들어왔을 때 이벤트 이름으로 구분이 되는 socket과 달리 직접 필터링을 해줘야 하는 부분이 존재했습니다. 그렇기에 저희는 기존에 사용하던 socket을 그대로 이용하기로 결정했습니다.
-
라이브러리 사용하지 않고 차트 구현 과정.
- 라이브러리 없이 직접 차트를 그리기 위해 가장 대표적인 차트 그리는 라이브러리인
chart.js
와 주식관련 서비스에서 가장 많이 사용 중인TradingView
를 분석해 봤습니다. - 해당 라이브러리들을 조사한 결과 차트를 세분화하여 Canvas API를 이용해 그리는 것을 알 수 있었고 canvas를 사용하는 것이 svg나 Dom 요소를 이용해 그리는 방식보다 빠르다고 알게 되어 Canvas API를 이용해 직접 그리는 방향으로 진행했습니다.
- 차트를 직접 구현하지만, 최대한 주식 차트의 기능들을 온전히 구현하기 위해 노력했으며, 마우스 위치에 따른 값을 표기하는 기능과 2개의 차트 높낮이를 사용자가 동적으로 수정하는 기능, 차트를 원하는 기간에 맞게 보여주는 기능을 구현했습니다.
- 라이브러리 없이 직접 차트를 그리기 위해 가장 대표적인 차트 그리는 라이브러리인
(8분25초)
-
vite proxy server로 로컬에서 쿠키인증하기
- 저희 서비스는 카카오 oAuth를 활용해 인증을 처리합니다.
- oAuth 로그인을 성공한 후 받은 쿠키를 활용해 API를 요청할 때마다 인가 과정을 거치게 됩니다.
- 그러나 로컬에서 서버로 API를 요청할 때 쿠키가 자동으로 담기지 않는 문제가 있었습니다.
- 로컬 도메인(localhost)과 서버 도메인(111.111.111.11)이 달라서 쿠키들이 담기지 않았습니다.
- 브라우저의 SOP(Same-Origin-Policy)에 따라 도메인이 다르면 쿠키가 전송되지 않는 것이었습니다.
- 이 문제는 vite에서 제공해주는 proxy server를 통해 해결할 수 있었습니다.
- 큰 흐름은 다음 그림과 같습니다.
- 우선 vite.config.ts에 vite proxy server를 설정합니다.
- 그 후 그림에서 볼 수 있듯이 서버로부터 개발용 임시 토큰을 받는 API를 호출해 임시 토큰을 받아옵니다.
- 응답 받은 토큰을 직접 js로 document.cookie를 활용해 쿠키를 설정합니다. localhost에서 설정했기 때문에 쿠키의 도메인은 localhost가 됩니다.
- 브라우저에서 vite proxy server로 통신할 때 같은 도메인(localhost)이기에 쿠키가 잘 전송됩니다.
- vite proxy server는 vite.config.ts에 정의한 서버 주소와 통신하게 되는데 server 간 통신이기 때문에 쿠키가 자동으로 전송 됩니다.
- 이를 통해 로컬에서도 원활하게 api 테스트 할 수 있는 환경을 구축해 개발 생산성을 높일 수 있었습니다.
-
소켓 연결 제한 이슈
(11분 10초)
-
서비스 시연(데모 시나리오 하듯이) (6분)
- 기능 소개
- 메인페이지 들어오면 주가지수 4개 실시간으로 바뀌는거 언급
- 전체 코스피/코스닥/코스피 200 이거 눌러서 바뀌는거 보여주고
- 주요 뉴스 이렇게 태그로 구분되어 있고, 누르면 링크 접속되는거 보여주기
- 로그인하기
- 삼성, 네이버 같은거 검색해서 검색 결과 잘 뜨는거 보여주고
- 이거 검색 히스토리 남는 것도 보여줘야 함. 검색 두번해야 함
- 삼성 전자 디테일 페이지 들어가서
- 차트 일/주/월/년 왔다갔다 하고 마우스 올려서 가격 막 움직이는거 보여주고 5일 20일 차트도 언급하고 5일 20일 차트 껐다 켰다 해주기. 왼쪽 위에 일마다 시가 종가 고가 저가 잘 보이는거 언급
- 실시간 시세 잘 올라오는거 언급해주고 일별 탭도 들어가서 언급
- 매수/매도
- 매수/매도 할 때 말도 안되는 숫자(1, 99999999) 입력했을 때 상한가 하한가 고정되는거 보여주고
- 하한가로 한번 구매, 정상가로 한번 구매하기
- 즐겨찾기
- 즐겨찾기 버튼 누르면 하트 채워지고고
- 마이페이지
- 마이페이지에서 자산 현황 5초에 한번씩이었나 바뀌는거 확인
- 주문 요청 현황에서 아까 하한가로 사서 구매 안된 친구 취소하기
- 즐겨찾기에서 아까 즐겨찾기 한 애들 확인 가능하고, 클릭하면 그 페이지로 넘어간다는 거 정도만 보여주고 다시 마이페이지로 돌아오기
- 내 정보에서 이름 변경! 끝!
- 랭킹
- 원래는 장 마감되고 한번에 바뀌는데, 지금은 시연을 위해서 1분에 한번 업데이트로 바꾸어두었음 언급
- 랭킹 들어오면, 수익률 기준으로 랭킹 하나 자산 기준으로 랭킹 하나 있는거 보여주기. top 10 까지 보여주고, 추가로 하단에 내 순위 보여주는거 언급
(17분09초)
- [FE] 프론트엔드 기술스택
- [FE] 라이브러리 없이 차트 구현 이유
- [FE] Canvas API 사용방법
- [FE] 네비게이션 바 애니메이션 구현
- [FE] Socket.io 사용방법
- [FE] Tanstack Router에 대하여...
- [FE] Intl(Internationalization) API
- [FE] React Suspense 적용
- [FE] 한글 입력 방식의 유연성을 높인 검색 시스템 구현하기
- [BE] 백엔드 기술 스택
- [BE] SSE vs Socket.io
- [BE] Redis를 도입하게 된 계기
- [BE] ACG Rule을 활용한 Secure CI CD 파이프라인 구현
- [BE] Nginx 로드밸런싱을 통해 한국 투자 API 소켓 제한 극복
- [BE] 주가 지수 기능 개발 과정
- [BE] 매수 및 매도 기능 개발 과정
- [BE] 실시간 자산 조회 기능 개발 과정
- [BE] 단위 테스트
- [BE] redis를 이용한 한국투자 Open API 세션 관리
- [BE] 데이터베이스 인덱싱
- [FE] React에서의 DOM 요소 접근 (useRef vs getElementById)
- [FE] Outlet을 활용한 공통 레이아웃 관리
- [FE] react hooks가 특정 조건에서 실행되면 안되는 이유 & useQuery에 query function 매개변수가 undefined일 수도 있을 때 어떻게 해결할까
- [FE] cross‐domain 로컬 환경에서 cookie로 인증 처리하기 with vite proxy
- [FE] 크롬&사파리 Composition 차이
- [FE] useEffect 의존성 배열
- [BE] Naver Cloud Platform HTTPS 무응답 현상
- [BE] 한국투자 Open API에서 access token을 발급받지 못하는 문제
- [BE] 한국투자 Open API와 웹소켓 연결이 되지 않던 문제
- [BE] 한국투자 Open API 웹소켓 연결이 중단되는 문제
- [BE] 같은 주식 주문이 동시에 여러 번 체결되는 문제
- [BE] 한국투자 Open API Websocket 세션을 두 개에서 한 개로 변경하기
- [BE] Nginx 로드 밸런싱 중 Socket bad Request 발생하는 현상
- [BE] 매수/매도 체결 로직에 의해 redis pub/sub이 정상적으로 동작하지 않는 문제