Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
mclub4 authored May 22, 2024
1 parent 843ab2c commit 83e5e23
Showing 1 changed file with 29 additions and 29 deletions.
58 changes: 29 additions & 29 deletions back/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# 💾 Backend 기술 문서

### 기능적 고려 사항
## 기능적 고려 사항

#### 채팅 구현
### 채팅 구현

채팅 서버는 크게 Polling, Long Polling, Steaming, Websocket 방식으로 구현할 수 있다. 앞선 3가지 방법은 일반적인 RESTFUL API를 이용한 구현 방법이다. 그래서 비교적 구현이 쉽다. 하지만, 이들의 특성상 클라이언트 → 서버로 데이터 전송이 가능하지만, 서버 → 클라이언트로 전송은 불가능하다. 또한, 100% 실시간성을 보장하지 않는다. 이를 극복하기 위해서 나온 방법이 바로 4번 WebSocket 방식이다.

Expand All @@ -23,9 +23,9 @@
현재 크롤링은 학식과 공지사항 분야에서 실시되고 있다.


### 성능적 고려 사항
## 성능적 고려 사항

#### 캐싱
### 캐싱

유저들이 자주 접근하는 데이터는 캐싱하는 것이 좋다.
예를 들어서, 공지사항 목록이나 Q&A 목록인 경우, 자주 바뀌지도 않을텐데 유저들이 해당 게시판에 접속할 때마다 매번 RDB에서 목록을 읽어오는 것은 비효율적이다.
Expand All @@ -50,7 +50,7 @@ Write 전략은 Write Around 전략을 활용했다. 데이터를 쓸 때, Redis

<br>

#### 비동기 처리
### 비동기 처리

모든 요청을 동기적으로 처리하는 것은 매우 비효율적이다. 특히, 이미지를 업로드하거나, 번역 API 같은 다른 API에 요청을 보내거나, 크롤링 등은 매우 오래걸리는 작업이다. 이 모든 작업을 동기적으로 처리하면, 성능에 매우 치명적일 것이다. 그래서, 해당 기능들을 사용하는 경우에는 Async를 통한 비동기 처리를 실시하였다.

Expand All @@ -60,21 +60,21 @@ Write 전략은 Write Around 전략을 활용했다. 데이터를 쓸 때, Redis

그래서, 위 사진과 같이 Async의 Executor를 ThreadPool방식의 Executor로 설정했다. 이는, 사용할 스레드 풀에 속한 기본 스레드 수인 corepoolsize, corepoolsize가 가득 찬 상태에서 더이상 추가 처리가 불가능 할때, 대기하는 장소 크기인 queuecapacity, 스레드 풀이 확장될 수 있는 스레드의 상한선, 즉 스레드 수의 상한선인 maxpoolsize를 조정하여 ThreadPool방식의 Async Executor를 구성했다.

#### Race Condition 해결
### Race Condition 해결

프로젝트에서 답변의 추천기능을 구현하면서 추천 / 추천 해제 기능을 만들었다. 문제가 있다면 추천수, 조회수와 같은 기능은 하나의 필드에 대해 수많은 트랜젝션 요청이 오가는 기능이기 때문에 이경우 자원에 접근하는 것에 대한 Race Condition, 동시성 문제가 발생하게 된다. 이러한 경우를 해결하기 위해서 Redis를 사용하였다. Redis는 싱글 스레드로 자원에 대한 Race Condition 문제를 해결할 수 있기 때문이다. Redis에 요청을 보낼 countTemplate를 선언하고 이를 통해서 Redis에 객체와 답변의 추천수를 저장하고 일정시간마다 이렇게 Cache에 저장된 내용을 DB에 반영시키도록 하였다. 이렇게 Redis에 저장된 변경 사항은 30초 간격으로 DB에 반영되는 방식으로 구현하였다.

현재 방법도 동시성 문제를 해결하는 방법이지만 내가 추천한 답변의 추천수가 즉시 반영되어 보여지지는 않는다. 이는 데이터를 읽어올때는 캐시에서 찾는 방식이 아니기 때문이다. 추후에는 캐시에서 값을 찾도록 구현하고 Redis Lock과 Redis Trasaction을 사용한 관리나 RDS의 트랜잭션 단위를 조절하는 방법도 고려해봐야 한다.

#### N+1 문제
### N+1 문제

여러 참조관계를 가진 테이블이 존재하고 비즈니스 로직에서 이들을 Join하여 호출하는 상황이 많이 있었으며 이때 N+1문제가 발생하였다. 이를 해결하기 위해 Fetch Join을 사용하거나 Projection 주입을 사용하는 경우의 Inner Join과 같이 여러 경우의 N+1의 해결 방법을 모색하여 적용하였다. 이로 인해 서버 비즈니스 로직의 성능향상이 이루어질 수 있었다. 이 과정에서 여러 시행 착오가 있었다. 기존의 방식은 Projection을 사용하여 DTO를 바탕으로 JPA 검색을 사용하는 방식이었는데 이 경우 Fetch Join이나 EntityGraph로는 해결이 불가능하다. @Projection을 사용한 해결 방안도 있으나 좀 더 고민해본 결과 Join의 목적에서 N+1문제를 야기할 필드값이 큰 문제가 없어 InnerJoin으로도 충분히 해결되는 범주였다는 것을 깨달았다.

### 안정성 고려 사항
## 안정성 고려 사항

<br>

#### 사용자 트래픽 제한
### 사용자 트래픽 제한

A라는 사람이 악의적으로 우리 AI 모델에게 1분에 100만번의 요청을 보낼 수 있다. 그러면 우리 AI Token은 모두 사라진다. B라는 사람은 Spring 비즈니스 서버에 1초에 1000만번 요청을 날린다. 그러면, 역시 서버가 터질 것이다. 따라서, 서버와 클라이언트 사이에서 미들웨어 역할을 하여 분당 API 사용량을 제한할 필요가 있다. 그래서 API Rate Limiter를 적용했다.

Expand All @@ -95,21 +95,21 @@ Redis는 인메모리 DB이기 때문에 매우 빨라서 적합하다. 또한,

<br>

### 보안적 고려 사항
## 보안적 고려 사항

<br>

#### 사용자 비밀번호 암호화
### 사용자 비밀번호 암호화

사용자의 비밀번호를 그대로 DB에 보관하는 것은 잘못된 일이다. 비밀번호를 암호화하지 않고 저장하면, DB가 해킹당했을 경우 큰 피해가 발생한다. 실제로, 비밀번호 암호화는 젤 기초적인 작업임에도 불구하고 하지않아 개인정보유출에서 큰 피해를 본 기업들이 많이 있다.

그래서, “외국민” 서비스는 Firebase를 이용하여 사용자의 ID와 비밀번호를 관리한다. Firebase는 비밀번호를 암호화해서 안전하게 잘 보관해주기 때문이다.

#### JWT를 통한 Authentication
### JWT를 통한 Authentication

Token은 서버가 각각의 클라이언트가 누구인지 구별할 수 있도록 사용자의 유니크한 정보를 담은 암호화된 데이터이다. 우리 서비스의 여러 기능들은 인증된 사용자만 접근 할 수 있도록 하는 것이 정상일 것이다. 따라서, 로그인시 Main Business Server에서 Access 및 Refresh Token을 발급해주고, 매 요청마다 Header로 AccessToken을 보내야 한다. 그러면 API Gateway서버에서 해당 토큰을 확인해보고, Invalid한 JWT Token일 경우 403 에러를 보내준다. 정상적인 Token일 경우, 해당 서버로 라우팅을 해주어 요청을 처리하도록 구성했다.

#### Token 탈취 상황 예방
### Token 탈취 상황 예방

JWT토큰은 탈취의 위험성이 있다. JWT기반 인증 방식에서는 세션과 다르게 Stateless하다는 특징이 있기 때문에, Token이 탈취당해도 우리 서버가 그걸 알아차리고 해당 Token을 중지시킬 수 있는 방법은 존재하지 않는다. 따라서, accessToken은 유효기간이 짧은 단위의 토큰으로 구성해야 한다. 주로 15~30분 정도로 구성하는 경우가 많다. 하지만, 이렇게 짧은 시간으로 설정한다면, 사용자는 15~30분마다 계속해서 재로그인을 해야된다는 번거로움이 있다. 그래서 등장한 것이 Refresh Token이다.

Expand All @@ -129,67 +129,67 @@ Refresh Token은 AccessToken과 다르게 매우 긴 유효기간을 가지고

Refresh Token Ratation(RTR)이란 Access Token이 만료되고 Refresh Token으로 새로운 Access Token을 받아올 때, 새로운 Refresh Token도 받아오도록 하는 방법이다. 즉. Refresh Token이 사용될 때마다 새로운 Access Token과 Refresh Token을 발급하여 이전에 발급된 Token들은 사용이 불가능하도록 한다.

#### AccessToken Ban
### AccessToken Ban

만약에 사용자가 로그아웃을 했다고 해보자. AccessToken의 기간이 만약에 30분인데, 5분만에 로그아웃을 했는데도, 그 Token가지고 요청을 보낼 수도 있다. 따라서, 로그아웃을 할 경우, Redis에 해당 AccessToken을 Ban을 해두어서 요청을 보내지 못하도록 설정했다.

#### HMAC
### HMAC

우리 서비스의 대부분에 AccessToken을 요구하더라도, 로그인 및 회원가입 같은 경우는 Token이 없는 상황에서 발생하는 요청이다. 하지만, 누군가 우리 Endpoint를 알아내고, Flutter 앱이 아닌 다른 곳에서 요청을 보낸다면 이는 비정상적인 요청일 것이다.

<img src="https://github.com/Alpha-e-Um/Frontend/assets/55117706/588f068d-ddd3-44b4-bd0c-748d5739eb5c">

따라서, 회원가입 및 로그인 시에는 HMAC을 포함하여 요청을 보내도록 구성했다. 우리 Flutter 앱과 서버 모두 공통된 Secret를 가지고 있으며, 이 Secret을 가지고 우리 Request의 HMAC을 같이 보내도록 구성했다. 그러면, 우리 앱에서 보내지 않은 요청들은 모두 막을 수 있을 것이다.

#### 환경변수 적용
### 환경변수 적용

JWT Secret, HMAC Secret, API Key 등등을 우리 Code에 Hard Coding해서는 절대 안된다. 따라서, env 파일로 환경변수를 분리하여 노출되지 않도록 설정했다.

<br>

### 테스트 고려 사항
## 테스트 고려 사항

#### JUnits
### JUnits

JUnit은 자바 프로그래밍 언어를 위한 오픈 소스 단위 테스트 프레임워크로, 개발자가 자바 애플리케이션의 소프트웨어 테스트를 자동화하고 효율적으로 수행할 수 있게 합니다. JUnit은 테스트 주도 개발(TDD)을 촉진하며, 코드 변경 시 발생할 수 있는 버그를 조기에 발견하고 수정하는 데 유용하다. 주요 기능으로는 어노테이션을 사용한 간편한 테스트 정의, Test Suite을 통한 여러 테스트의 그룹화, 다양한 메서드를 통한 테스트 결과 검증, 테스트 실행 중 예외 상황 처리, 그리고 테스트 실행 결과를 시각적으로 보여주는 리포트 기능 등이 있다. JUnit은 모듈화된 코드 테스트를 장려하며, 지속적 통합(CI) 도구와의 통합을 통해 자동화된 빌드 프로세스에서도 중요한 역할을 한다. 직관적이고 사용하기 쉬운 API 덕분에 개발자들이 테스트 케이스를 작성하고 유지보수하는 데 걸리는 시간을 줄이는 데 도움이 된다.

#### Test Container
### Test Container

Test Containers는 통합 테스트에서 사용되는 자바 라이브러리로, Docker 컨테이너를 통해 데이터베이스, 메시징 큐, 웹 서버 등의 외부 리소스를 손쉽게 설정하고 관리할 수 있다. 이를 통해 개발자는 테스트 환경을 실제 운영 환경과 유사하게 구성할 수 있으며, 테스트의 신뢰성과 재현성을 높일 수 있다. 주요 특징으로는 코드 내에서 직접 Docker 컨테이너를 제어할 수 있는 API 제공, 다양한 프리셋과 유연한 구성 옵션, 테스트 실행 시 필요한 컨테이너를 자동으로 시작하고 종료하는 기능, 각종 데이터베이스(MySQL, PostgreSQL 등)와의 통합 지원, 그리고 쿠버네티스 환경에서도 사용 가능한 확장성 등이 있다. Test Containers는 JUnit과 같은 테스트 프레임워크와도 통합되어, 단위 테스트와 통합 테스트를 쉽고 일관성 있게 작성할 수 있다는 장점도 있다.

우리가 실제 서비스하는 RDS를 가지고 테스트를 진행한다면, 매우 위험할 것이다. 따라서, Test Container를 이용하여 독립된 환경에서 안전하게 테스트를 진행하도록 하였다.

#### Apache Jmeter
### Apache Jmeter

Apache JMeter는 자바 기반의 오픈 소스 소프트웨어로, 웹 애플리케이션 및 다양한 서비스의 성능 테스트와 부하 테스트를 수행하기 위해 사용된다. JMeter는 초기에는 웹 애플리케이션을 테스트하기 위해 개발되었으나, 현재는 데이터베이스, FTP 서버, 웹 서비스, JMS, LDAP, SMTP, POP3, IMAP 서버 등 다양한 프로토콜 또한 지원한다.

프로젝트에서 동시성 문제 테스트와 같이 대규모 부하 테스트를 위해서 사용하게 되었다.

<br>

### 아키텍처 설계
## 아키텍처 설계

<img src="https://github.com/kookmin-sw/capstone-2024-30/assets/55117706/bc51c38d-aa69-4b6c-a152-7d5cc7d197a4">

### 현실적 제한 및 개선 필요 사항
## 현실적 제한 및 개선 필요 사항

<br>

#### 검색 기능 개선
### 검색 기능 개선

현재 게시물 및 공지사항 검색은 제목으로만 검색된다. 하지만, 이보다 제목 + 내용으로 검색이 가능한 것이 더 좋을 것이다. 이를 위해서 SQL Like 연산자로 내용까지 검색이 가능하긴 하나, Like 연산자는 선형 연산자이기 때문에 성능에 매우 치명적이다. 따라서, ElasticSearch를 이용해서 빠르게 검색하는 것이 더 좋을 것이다. ElasticSearch는 분산 검색 및 분석 엔진으로, 대규모 데이터에서 빠른 전체 텍스트 검색을 지원한다. 추후, ElasticSearch를 통해 우리 RDB에 저장된 글들을 역색인하여 거의 실시간에 가깝게 검색하도록 개선할 것이다.

<br>

#### 인스턴스 성능의 한계
### 인스턴스 성능의 한계

<img src="https://github.com/kookmin-sw/capstone-2024-30/assets/55117706/61f41786-00e2-4ae2-b059-9bbe080edc91">

현재 우리서버는 Free Tier를 사용하여 견딜 수 있는 트래픽의 한계가 있는 상황이다. 이를 해결하기 위해서 수직적 확장 또는 수평적 확장을 고려할 수 있다. 현재 Free Tier 성능이 아닌 더 좋은 EC2 인스턴스를 사용하거나, 여러개의 EC2를 만들어 Load Balancing을 적용하여 Scale Out적인 확장을 할 수도 있다. 하지만, 현재는 비용적인 문제로 불가능한 상황이다.

<br>

#### 무중단 배포
### 무중단 배포

현재 우리 서비스는 Git Actions를 이용한 CI/CD를 적용하고 있다. 하지만, 현재 방식은 새 버전으로 서버가 배포될 때 반드시 1~2분정도 서버가 중단될 수 밖에 없다. 이는, 실제 서비스할 때 매우 치명적이다. 따라서, 무중단 배포가 반드시 필요하다.

Expand All @@ -203,19 +203,19 @@ Apache JMeter는 자바 기반의 오픈 소스 소프트웨어로, 웹 애플

<br>

#### 로드 밸런싱
### 로드 밸런싱

서버를 단일로 구성하면, 많은 트래픽이 몰렸을 때 감당하기 힘들 것이다. 따라서, 서버를 여러개 만들고, Load Balacning하는게 좋은 선택지일 것이다. 하지만, 로드 밸런싱을 하기엔 비용적인 문제 때문에 환경 구축이 어렵다. 왜냐하면, 여러개의 EC2를 띄워야되기 때문이다. 그래서, 어쩔 수 없이 단일 서버로 운영하게 되었지만, 추후 사용자가 늘어나면 로드 밸런싱을 적용할 계획이다.

<br>

#### 캐싱
### 캐싱

이미지를 받아오는 S3도 캐싱을 무조건 해야한다. 아니면 S3 비용이 굉장히 많아질 것이고 응답시간이 길어진다. 일반적으로 AWS CloudFront같은 CDN 서버를 두는 경우가 많은데, 이 부분은 시간이 부족해서 하지 못했다.

<br>

#### 모니터링 및 로깅 강화
### 모니터링 및 로깅 강화

<img src ="https://github.com/kookmin-sw/capstone-2024-30/assets/55117706/ced776d3-c974-4365-956e-f1c598ecbd7b">

Expand All @@ -227,7 +227,7 @@ Apache JMeter는 자바 기반의 오픈 소스 소프트웨어로, 웹 애플

<br>

#### 컨테이너 오케스트라제이션
### 컨테이너 오케스트라제이션

<img src="https://github.com/kookmin-sw/capstone-2024-30/assets/55117706/5d3fbe14-adef-4439-b950-e613262038c0">

Expand Down

0 comments on commit 83e5e23

Please sign in to comment.