백엔드 개발 포트폴리오 | 개발자 커뮤니티

내일배움캠프 4기 웹 개발 과정 Spring 트랙 수료생 최종 프로젝트 ‘디벨로니티’을 소개합니다.
Aug 17, 2023
백엔드 개발 포트폴리오 | 개발자 커뮤니티

코드 컨벤션 & RESTful API

RESTful API 참고 Ref - https://sanghaklee.tistory.com/57

프로젝트 개요

주제 및 선정 배경

Developer + Community = Develonity
  • 입문 & 주니어 & 시니어 개발자 모두에게 가치 있는 커뮤니티를 만들고자 했습니다.
  • Q&A 게시판 활동(답변)을 통해 Gift Point 를 쌓고, 포인트로 Gift Card 를 살 수 있습니다.
  • [미구현] Q&A 게시판 활동(답변)을 통해 단순히 Gift Point 뿐만 아니라 Respect Point 가 쌓이게 되고, Respect Point 를 기반으로 외주, 과외 등의 서비스 를 제공하고자 했습니다.
 

프로젝트 팀 구성 및 역할

리더
  • User(Account) & Spring Security - redis & JWT 기반의 Token 인증 및 Admin과 User의 Filter&Authentication 로직 구분
  • 전반적인 프론트 제작 및 연동(Gift Card & Order쪽 프론트 제외)
  • 프론트(S3 + CloudFront), 백엔드(EC2), RDS(Mysql) 배포
  • Github Actions 기반 CI 적용
  • Sub Module & profile 기반 배포, 테스트 환경 분리
 
부리더
  • 기프트카드 관련 백&프론트 로직 구현
    • 기프트 카드 등록, 조회, 수정, 삭제
  • 주문 관련 백&프론트 로직 구현
    • 주문하기, 주문조회
  • 메인페이지 제작(프론트엔드)
 
팀원
  • 게시글 관련 백엔드 로직 구현
    • 게시글 CRUD 기능
    • 좋아요 기능
    • 스크랩 기능
    • 답변 채택, 포인트 및 등업 관련 기능
  • QueryDSL을 활용한 조회 및 검색, 정렬 기능 구현
  • AWS S3를 활용한 이미지 업로드 기능 구현
 
  • 댓글 관련 백엔드 로직 구현
    • 댓글 CRUD 기능
    • 좋아요 기능
    • 대댓글 기능
 
 
  • 어드민 관련 백엔드 로직 구현
    • 유저 전체 조회
    • 유저 ROLE에 따른 조회
    • 유저 개인 조회

주차별 프로젝트 타임라인

 

JIRA를 활용한 프로젝트 업무관리

notion image
notion image
notion image

API 문서(Postman Publish Documentation)


서비스 아키텍쳐

notion image
 

MVP 시연

→ 이미 세팅이 되어있는 상태로 간략하게 슥슥 (2분언더)
[로그아웃 → 로그인 → QnA 슥 돌고 → 상세 조회(채택할 댓글 세팅 된)에서 댓글&대댓글&좋아요&채택 풀코스 → 기프트카드 조회 & 구매 → 기프트카드 구매내역 → 회원탈퇴 → 로그인실패 ]
++++ 기프트카드 등록, 수정 페이지도 보여주기
 

프론트 미구현으로 빛을 보지 못한 서비스들…

  • 게시물 스크랩 등록/취소/조회
  • 검색 기능
  • 등업 기능
  • 내가 쓴 게시글 , 댓글 조회 등 활동 조회
  • 인기글(당일 작성된 좋아요 순 게시글 3개)
  • 커뮤니티 게시판
  • 어드민
 

기술적 의사결정

 
Session vs Token(JWT)
  1. 보안 vs 효율&확장성
  • 서비스의 특성상(커뮤니티) 보안적으로 매우 민감한 주제는 아니라고 판단하여
  • HTTP의 비상태성(Stateless)를 그대로 활용할 수 있고, 따라서 높은 확장성을 가질 수 있는 Token방식을 채택하였다.
 
  1. 그럼에도 보안을 챙기긴 해야한다.
  • refresh token을 도입하여 access token의 유효기간을 짧게 가져가고,
  • RTR(refresh token rotation)을 도입하여 refresh token 탈취 시 문제점도 어느정도 보완,
  • 계정정보 or 회원탈퇴 등 중요한 기능들은 ‘비밀번호 검증'을 1회 더 하는 방식으로 보안을 조금 더 강화하였음.
++ access token과 refresh token이 모두 탈취 당했을 경우에 대한 고민
 
현재 프로젝트에서 대응 가능한 방법들
  1. RTR(refresh token rotation)을 적용하고 access token의 유효시간을 30분으로 짧게 설정해두어, 사용자가 서비스를 이용중이라면 [reissue 실패 → 재로그인 → 탈취된 refresh token 무효화]가 가능하다.
  1. 지속적으로 탈취되는 상황이라면 해당 회원을 일단 탈퇴처리 하여 해커의 나쁜 행동을 막고 개인적으로 네트워크와 컴퓨터를 리셋(포맷)한 이후에 다시 탈퇴 취소 하는 방법이 있긴하다. (회원 탈퇴는 soft delete 방식으로 구현되어 가능하다.)
  1. 특정 상황이 발생하여 회원 다수의 token이 탈취된 경우라면 30분여 서비스 점검 실시(access token 유효기간 만료를 위함) 및 redis(refresh token 저장소)를 재가동 하는 방법도 고려할 수 있을 것 같다. (서비스 점검기간동안 보안적인 대처도 병행)
  1. 현재 계정정보 접근, 회원탈퇴 등 민감한 서비스들에는 패스워드 재검증 로직을 포함하고 있으므로 개인정보 유출, 금전적 피해 가 발생 가능한 경우는 예방하고 있다.
 
Redis - Token 저장소로 Redis를 선정한 이유
  1. Key(LoginId)-Value(Refresh Token) 외의 다른 필드가 필요하지 않다.
  1. I/O가 빈번하게 발생하는 환경
  1. 저장된 데이터의 개수와 무관하게 O(1) 의 수행시간을 가진다.
  1. 저장된 모든 token을 조회하는 등의 싱글 스레드의 단점이 부각 될 상황이 없다.
 
Admin과 User 분리
기존 방식
  • 동일한 User Entity에서 Role Enum 으로 Admin과 User를 구분하였음
  • 동일한 security filter와 Authentication Service를 사용하였음.
 
문제점
  • 근본적으로 User와 Admin의 생명주기가 다르고,
  • User의 다양한 field들은 Admin에게 불필요함.
  • User와 Admin의 인증과정을 분리할 수 없음.
 
해결
  • Entity 및 Package 분리
  • security filter와 Authentication Service 분리
  • Authentication Service에 팩토리 패턴 적용하여 OCP 원칙을 지키고자 노력
 
 
게시글 JPA 상속 관계 매핑
 
JPA는 테이터베이스와 객체를 매핑해주는 자바 진영의 ORM 기술 표준이지만 객체의 상속 관계와 정확하게 일치하는 데이터베이스 모델링은 존재하지 않는다.
 
따라서 차선책으로 상속 관계와 비교적 유사한 슈퍼타입-서브타입 모델링 기법으로 데이터베이스를 상속 객체에 매핑 해야 한다.
 
슈퍼타입-서브타입 논리모델을 실제 데이터베이스 물리모델로 구현하는 방법으로 3가지 전략이 있는데 그 중 단일 테이블 전략을 사용했다.
 
단일 테이블 전략의 장점은 아래와 같다.
  • 조인이 필요 없으므로 조회 성능이 빠르다.
  • 조회 쿼리가 단순하다.
단일 테이블의 단점은 아래와 같다.
  • 하위 엔티티의 필드값은 모두 Null을 허용한다.
  • 하나의 테이블에 칼럼이 많아져 복잡하다.
 
단점이 있음에도 단일 테이블 전략을 선택한 이유는 아래와 같다.
 
  • 지금 진행 중인 프로젝트는 게시글의 필드값을 최대한 적게 가져가는 방식을 사용하고 있어서 테이블 칼럼 수가 적고 가볍기 때문에 단일 테이블 전략을 사용하더라도 복잡해지지 않는다.
  • 비록 데이터베이스에는 Null값을 허용하더라도 실제 객체가 Null값을 가지고 있는 것은 아니다.
 
만약 단일 테이블 전략이 아니라 각자 테이블을 가지는 조인 전략과 같은 방식을 사용했다면 Null값은 들어가지 않지만 조회 쿼리가 복잡해지고 INSERT QUERY를 2번 실행해야 하기 때문에
단일 테이블 전략을 사용하기로 결정했다.
 
id를 활용한 간접참조 방식 설계
  • 직접 참조 : Entity 클래스를 설계할 때 @OneToOne, @OneToMany ... 와 같은 어노테이션을 써서 Entity 간에 연관 매핑하는 것
  • 간접 참조 : 객체를 직접 참조하지 않고, 식별값을 이용하는 것
 
  • 직접 참조 방식의 단점
    • 의존 관계 형성
    • 편한 탐색 오용 방지
      • 직접 참조를 한다면 연관 관계 맺은 객체를 편하게 탐색할 수 있고 바뀌길 원하지 않는 참조 객체의 값이 손쉽게 바뀔 가능성이 있다. 직접 참조는 편한만큼 위험한 것이다.
      • 예를 들어 Board Entity안에 user라는 변수가 있다면 Board를 다룰 때 User를 변경할 수 있는 가능성과 여러 실수의 가능성이 존재하게 됨
      • 즉 User 라는 Entity 자체를 날것으로 가져오게 되면, Entity 가 오염이 될 수 도 있음, setter 를 통해서든, 도메인 서비스를 통해서든 어떤 일이 벌어질 수 있는 가능성을 열어둔 것
  • 결론
User Entity도 안전하게 보호가 되고, Board Entity에만 집중할 수 있는 방법으로 간접 참조 방식을 이용하는 것이 나을 것이라고 판단했고,
간접 참조를 하면 의존 관계가 형성되지 않아서 추후 시스템을 확장할 때도 유리하기 때문에
간접 참조 방식을 이용해서 설계를 하게 됐다.
 

트러블 슈팅 & 성능개선

4조가 트러블 슈팅 or 성능개선을 하는 방식

  • (안건제시 → 문제분석 → 개선계획수립 → 코드 수정 → PR을 통해 검토 → 반영)
  • 실제 예시 링크(아래)
 
CI - sub module & profile & embedded redis 기반의 배포, 테스트 환경 분리
 
PresignedURL
notion image
 
 
기존 Aws S3 이미지 업로드 방식은
클라이언트 → 서버 → AWS S3로 이미지가 거쳐가기 때문에 서버에 부하가 걸릴 수 있습니다.
 
 
notion image
하지만 PresignedURL 방식을 활용하면
서버에서는 인증 작업만 해주고 위와 같이 클라이언트 → AWS S3로 바로 이미지 업로드가 가능한 링크만 제공해주면 되기 때문에 서버의 부담을 줄일 수 있습니다.
아직 단건 이미지 업로드 기능에만 적용 중입니다.
QueryDSL
 
notion image
위와 같이 Jpa Repostory를 이용해서 조회 기능을 구현 하기에는 검색 조건 등 여러 부분에서 불편함이 발생해서 프론트와 연결 시에도 비효율적인 메소드들을 추가로 작성해줘야 했습니다.
 
notion image
하지만 QueryDSL을 활용, 동적 쿼리를 작성해서 편하게 구현이 가능해졌습니다.
 

추후 개발 및 기술적인 도전 계획

프로젝트 주제를 어찌보면 무난한 주제로 선정한 가장 큰 이유 → 다양한 공부를 해보기에 좋은 환경

1. 서비스 측면으로의 확장

  • 게시글 스크랩 기능 [프론트 미구현]
  • 커뮤니티 게시판 [프론트 미구현]
  • 나의 활동 조회 (내가 작성한 게시물, 댓글 조회, 내가 채택 된 )
  • 등급제도(Respect Point) 보완
  • 등급제도 기반의 과외 & 외주 구인구직 게시판
  • Javax Mail API 를 활용한 회원가입시 메일 인증, 비밀번호 찾기, 중요 알람 서비스 등 구현

2. 기술적인 도전

  • CD(Continuous Deployment)
  • 대용량 트래픽 테스트
  • 동시성문제 (기프트카드로 테스트 가능)
  • 성능 분석 및 튜닝
  • 카카오 결제API 연동(test방식 / 실제연동x)
 
Share article
Subscribe to our newsletter
RSSPowered by inblog