Skip to content

Repository files navigation

💪 Go - Gym

스크린샷 2025-01-03 오후 12 49 07




😢헬스장 기간이 아직 남았는데 개인 사정으로 더 이상 다닐 수가 없나요? 고-짐이 도와드릴게요!
헬스장 이용권을 저렴하게 구입하고 싶으신가요? 고-짐이 도와드릴게요!

고-짐은 양도 게시판 기능을 제공하고 중고 거래를 불안해하는 이용자를 위해 안전 결제 기능을 도입했습니다. 
실시간 채팅을 이용해 쉽고 편하게 거래를 시작할 수 있어요!
사용하지 않는 헬스장 이용권을 양도하고, 저렴한 가격에 구매해보세요 🤗

💻 BE Repository : https://github.com/ProjectGoGym/BackEnd

💻 FE Repository : https://github.com/ProjectGoGym/front-end?tab=readme-ov-file

📆 개발기간 : 2024.11.18 - 2024.12.26 (총 6주)


같이 작업한 팀원

포지션 팀원 GitHub 링크
백엔드 전유노 GitHub
조하얀 GitHub
백승현 GitHub
최경호 GitHub
프론트엔드 전민혁 GitHub
박민주 GitHub

🎥 시연영상

👉 https://youtu.be/2i851kftF8Q 👈


📋 API 명세서

👉 https://documenter.getpostman.com/view/26575250/2sAYBd67yd 👈


🛠️ 프로젝트 아키텍쳐

아키텍쳐


🗺️ 전체 ERD

Go Gym-10

회원

스크린샷 2024-12-25 오후 1 13 57

채팅

스크린샷 2024-12-25 오후 1 14 41

결제

스크린샷 2024-12-25 오후 1 15 34

게시글

스크린샷 2024-12-25 오후 1 16 25

⭐️ 주요 기능

회원가입 로그인 소셜 로그인
회원가입 GoGYM-Chrome2024-12-2617-20-42online-video-cutter com-ezgif com-video-to-gif-converter 카카오 로그인
게시글 조회 및 필터, 찜 게시글 작성 및 수정 지역 관리 및 헬스장 데이터
게시물 조회2 글수정 지역검색2
마이페이지 짐 페이 충전 짐 페이 사용내역 조회
프로필자체 짐페이 짐페이 내역
실시간 채팅 안전결제 이미지 업로드
실시간 채팅 안전결제 이미지 업로드
실시간 알림 거래내역 조회 최근본 게시글
실시간 알림 거래내역조회 마이페이지

✨ 주요 기능 설명

  • ✨ 회원가입

    • 💡 신규 사용자가 서비스에 가입하여 계정을 생성할 수 있도록 회원가입 기능을 구현하였습니다. 이를 통해 사용자는 자신의 정보를 입력하고 인증을 완료한 후서비스를 이용할 수 있습니다.
    • 💻 구현한 기능
      • 요청 데이터 검증
        • SignUpRequest DTO를 사용하여 사용자 입력값(이메일, 비밀번호, 이름, 닉네임 등)에 대한 유효성 검증 수행합니다.
        • @Valid와 Hibernate Validator를 활용하여 필수 입력값 및 포맷 검증합니다.
      • 이메일 중복 체크 / 닉네임 중복 체크
        • MemberRepository를 사용하여 이메일 중복 여부를 확인합니다.
        • 중복된 이메일이 있을 경우 CustomException을 발생시켜 에러 메시지 반환합니다.
        • BanNicknameRepository와 MemberRepository를 활용하여 닉네임 중복 및 금칙어 검증합니다.
      • 비밀번호 암호화
        • PasswordEncoder로 SHA 를 사용하여 사용자 비밀번호를 안전하게 암호화한 후 DB에 저장하여 보안을 높였습니다
      • 회원 데이터 저장
        • SignUpRequest DTO를 Member 엔티티로 변환하여 사용자 정보를 DB에 저장
      • 회원가입 완료 후 추가 작업
        • 이메일 인증이 필요한 경우 인증 토큰 생성 및 이메일 발송을 진행합니다.
        • 이메일 발송은 JavaMailSender을 통해 구현했습니다.
  • ✨ 로그인

    • 💡 사용자가 안전하고 효율적으로 서비스를 이용할 수 있도록 로그인 기능을 구현하였습니다. 이를 통해 사용자는 이메일과 비밀번호로 인증받아 JWT 토큰을 발급받으며, 인증된 사용자로서 서비스를 이용할 수 있습니다.
    • 💻 구현한 기능
      • 로그인 요청 데이터 검증
        • SignInRequest DTO를 사용하여 사용자가 입력한 이메일과 비밀번호의 유효성을 검증하고 @Valid와 Hibernate Validator를 활용하여 필수 입력값과 포맷 검증합니다.
      • 사용자 인증 처리
        • MemberService를 통해 DB에서 이메일로 사용자 정보를 조회합니다.
        • PasswordEncoder를 사용하여 사용자가 입력한 비밀번호와 저장된 비밀번호를 비교하여 사용자 정보를 처리합니다.
      • JWT 토큰 발급
        • 인증이 성공한 경우 JwtTokenProvider를 사용하여 JWT Access Token을 생성합니다.
      • 응답
        • 로그인 성공 시 ResponseEntity를 통해 JWT Access Token과 Refresh Token을 헤더에 추가하여 반환합니다.
        • 사용자 기본 정보(ID, 이메일, 이름, 닉네임 등)를 포함한 LoginResponse DTO 반환합니다.
  • ✨ 소셜 로그인

    • 💡 카카오 OAuth 2.0 을 활용하여 소셜 로그인 기능을 구현하였습니다. 이를 통해 사용자는 카카오 계정을 사용하여 간편하게 회원가입 및 로그인을 수행할 수 있으며, 사용자의 편의성과 인증 과정을 간소화하였습니다.
    • 💻 구현한 기능
      • 카카오 OAuth 인증 URL 생성
        • KakaoService에서 사용자 요청 시 카카오 OAuth 인증 URL을 생성
        • Redirect URI를 설정하여 인증 완료 후 호출되는 콜백 URL 지정
      • Access Token 발급 및 사용자 정보 조회
        • 사용자가 카카오 인증을 완료하면 Authorization Code 로 Access Token을 발급
        • 카카오 API를 호출하여 사용자 프로필 정보를 조회
      • 회원가입 및 로그인 처리
        • 카카오 계정을 사용한 회원가입 처리, 기존 사용자 확인 및 Access Token 발급
        • 신규 사용자인 경우 KakaoMember와 관련된 사용자 정보를 저장
        • 기존 사용자일 경우 DB에서 이메일로 사용자 정보를 조회하여 로그인 처리.
      • JWT 토큰 생성 및 반환
        • 사용자 정보(email, ID, roles 등)를 포함한 JWT 생성
        • 로그인 성공 시 클라이언트에 JWT Access Token을 헤더로 반환
      • 카카오 사용자와 일반 사용자 구분 처리
        • 회원 데이터에 isKakao 플래그를 추가하여 카카오 소셜 로그인 여부를 구분
        • 일반 사용자와 카카오 사용자 간의 데이터 관리 차별화
      • 에러 처리 및 예외 상황 대응
        • 사용자가 카카오 인증에 실패하거나 잘못된 Authorization Code를 전달할 경우, 적절한 에러 메시지 반환
        • 카카오 API 호출 실패 시 로그를 기록하고 사용자에게 알림
  • ✨ 게시글 조회 및 필터, 찜 및 최근본 게시글 기능

    • 💡 게시글 조회, 필터링, 찜 기능과 최근 본 게시글 관리 기능을 구현하여 사용자가 관심 있는 게시글을 탐색하고 관리할 수 있도록 구현하였습니다. 이를 통해 서비스의 편의성을 높이고 사용자 경험을 향상할 수 있도록 설계하였습니다.
    • 💻 구현한 기능
      • 비회원 도 게시글 목록 및 게시글 필터링 기능을 사용할 수 있으며, 게시글 상세 조회 도 가능합니다.
      • 사용자가 설정한 관심 지역 에 해당하는 게시글 목록을 반환합니다.
      • 다양한 필터링 을 사용해 사용자 요구에 맞는 세부 조건 설정이 가능합니다.
      • QueryDSL 을 사용하여 복잡한 동적 쿼리문을 간결하고 유지보수 하기 쉽게 작성하였습니다. 이로써 다양한 조건을 기반으로 유연한 데이터 조회가 가능합니다.
      • 사용자는 게시글 상세 페이지 에서 찜 을 추가하거나 취소 할 수 있으며, 찜 상태 변화에 따라 게시글의 찜 횟수 를 동기화 하였습니다.
      • 찜 이 추가 될 경우, 해당 게시글 작성자에게 찜을 한 사용자와 게시글 제목을 포함한 실시간 알림 을 전송합니다.
      • 사용자가 게시글을 조회할 때, 해당 게시글을 최근 본 목록 에 추가하며, 동일한 게시글이 이미 목록에 있을 경우 중복 저장을 방지합니다.
      • 최근 본 게시글 목록은 최대 5개로 제한하며, 새로운 게시글 추가 시 가장 오래된 항목을 삭제처리 합니다.
  • ✨ 게시글 작성 및 수정

    • 💡 서비스의 주요 기능인 헬스장 회원권 거래 를 간편하게 관리할 수 있도록, 게시글 작성 및 수정 기능을 구현하였습니다. 이를 통해 사용자는 헬스장 정보, 거래 내용, 사진 등을 자유롭게 등록 및 수정하며 거래를 효율적으로 진행할 수 있습니다.
    • 💻 구현한 기능
      • 회원이 헬스장 정보 및 거래 조건을 입력하면 게시글이 생성됩니다. 등록 시, 헬스장 데이터 와 연동하여, 해당 헬스장의 위치 및 정보를 확인 가능할 수 있습니다. 게시글의 초기 상태는 거래 대기 상태 입니다.
      • 사용자는 게시글의 내용을 수정 할 수 있으며, 상태를 숨김 또는 거래 진행 상태로 변경이 가능합니다.
      • 작성 및 수정 시, 작성자 검증 및 게시글의 유효성을 검사하여 오류를 방지합니다.
  • ✨ 지역 관리 및 헬스장 데이터

    • 💡 거래 과정에서 헬스장 정보와 지역 데이터 를 정확히 연동하여, 게시글 필터링 및 회원이 설정한 관심지역 을 기반으로 사용자에게 신뢰도 높은 서비스를 제공하기 위해 해당 기능을 구현하였습니다. 이를 통해 사용자는 자신이 원하는 지역과 헬스장을 빠르게 선택할 수 있습니다.
    • 💻 구현한 기능
      • DB 기반 지역 데이터 를 저장하여 효율적인 필터링 및 연동을 제공합니다.
      • 상위 지역(도 / 시) 과 하위지역(구 / 군) 을 계층적으로 관리하여 데이터 정확성 을 높이고, 이를 기반으로 사용자 경험 개선을 하였습니다.
      • 헬스장 정보 를 지역정보 와 연결하여 관리합니다.
      • 위도, 경도 를 기반으로 헬스장 중복 여부를 확인하고, 없을 경우 새로 생성합니다.
  • ✨ 마이 페이지 정보 조회

    • 💡 사용자가 자신의 정보를 확인하고 관리할 수 있는 기능을 제공하기 위해 마이 페이지 정보 조회 API를 구현하였습니다. 해당 기능을 통해 사용자는 프로필, 결제 잔액, 지역 정보 등 개인화된 정보를 실시간으로 확인할 수 있습니다.
    • 💻 구현한 기능
      • 사용자 정보 조회
        • MemberService를 활용하여 사용자 ID로 DB에서 회원 정보를 조회
        • 사용자의 프로필(이메일, 이름, 닉네임, 전화번호 등)과 상태를 반환
        • JwtAuthenticationFilter를 구현하여 클라이언트의 요청 헤더에서 JWT를 추출하고 인증 정보(SecurityContext)를 설정
        • 예외 상황 처리(유효하지 않은 토큰, 만료된 토큰 등)
      • 지역 정보 조회 및 구성
        • 사용자의 지역 ID를 기반으로 RegionService를 호출하여 지역 정보를 조회
        • 지역 정보(도시, 구/군 )를 문자열 형태로 조합하여 사용자에게 반환
      • GymPay 잔액 정보 제공
        • 사용자의 GymPay 정보(잔액, ID 등)를 함께 반환하여 결제 상태 확인 가능
        • GymPay 데이터가 없는 경우에도 안정적으로 처리할 수 있도록 예외 처리 구현
      • 마이 페이지 API 응답 구성
        • MemberProfileResponse DTO를 사용하여 사용자 정보, 지역 정보, 결제 잔액 등을 클라이언트에 반환
        • JSON 형식으로 직렬화하여 프론트엔드와의 통신 표준화
      • 유효성 검증 및 예외 처리
        • 요청 ID에 대한 유효성 검증 처리.
        • 사용자의 상태가 DEACTIVATED인 경우 CustomException을 발생시켜 비활성 사용자 접근을 차단
        • 잘못된 요청이나 DB에 사용자 정보가 없는 경우, ErrorCode.MEMBER_NOT_FOUND 예외 처리
  • ✨ 짐페이 충전

    • 💡 안전결제를 도입하기 위해 자체 서비스의 포인트가 필요했습니다. 짐페이를 사용해서 안전 결제를 할 수 있도록 충전 기능을 구현하게 되었습니다.
    • 💻 구현한 기능
      • 금액을 입력하고 충전하기를 누르면 사전 결제정보가 redis에 저장됩니다. 사전 결제정보를 등록하는 PortOne api를 호출합니다.
      • 클라이언트에서 결제가 진행된 후 PortOne에서 Webhook을 받아서 검증을 진행합니다.
      • 결제 정보를 데이터베이스에 저장하고 결제 결과가 성공이라면 GymPay에 금액만큼 충전을 합니다.
  • ✨ 짐페이 사용 내역 조회

    • 💡 GymPay 잔액이 변동된 내역을 사용자가 확인할 수 있어야 한다고 생각해서 구현했습니다.
    • 💻 구현한 기능
      • GymPay 잔액이 변동될 때 GymPay History 테이블에 저장합니다.
      • History 테이블에 기록되는 순간 다른 거래가 진행되면 잔액이 정확하지 않을 수 있어서 분산락을 구현했습니다.
      • Redis 클라이언트를 Lettuce → Redisson으로 변경하고 tryLock()을 사용했습니다. Pa클라이언트가 요청하는 데이터를 Response에 내려줍니다.
      • Param으로 시작, 종료 날짜를 포함시켜 호출하면 해당 기간에 발생한 거래만 내려주고, 입력하지 않으면 전체 기록을 최신순으로 정렬해서 내려줍니다.
  • ✨ 유저 간 실시간 채팅

    • 💡 거래 과정에서 사용자 간 효율적이고 신뢰할 수 있는 의사소통을 제공하기 위해 채팅 기능을 구현하였습니다. 단순 메시지 송수신뿐만 아니라, 안전거래와 같은 System 메시지를 별도로 처리하여 거래 관련 정보를 명확히 전달하고, Redis와 WebSocket을 활용해 실시간성과 데이터 안정성을 확보하였습니다. 이를 통해 사용자는 빠르고 안정적인 채팅 환경에서 거래를 위한 소통을 원활히 진행할 수 있습니다.
    • 💻 구현한 기능
      • WebSocket을 활용한 실시간 메시지 전송
        • SimpMessagingTemplate 을 사용해 메시지를 WebSocket으로 브로드캐스팅하여 참여자들이 실시간으로 메시지를 수신할 수 있도록 구현.
        • 커스텀 핸들러(StompHandler)를 활용해 WebSocket 연결 시 아래 사항들을 처리.
        • 클라이언트가 전송한 Authorization 헤더의 JWT 토큰을 검증.
        • 유효한 JWT 토큰에서 사용자 ID를 추출하고, Principal 객체를 생성하여 STOMP 세션에 사용자 정보를 저장.
        • WebSocket 통신에서 클라이언트를 명확히 식별할 수 있도록 사용자 정보를 설정.
      • 채팅방 생성 시 초기 메시지 전송
        • 채팅방 생성 시 요청자가 자동으로 "{닉네임}님이 채팅을 요청하였습니다." 메시지를 보낸 것처럼 처리.
        • Spring Event 기반의 이벤트 를 통해 초기 메시지를 생성하고, Redis 및 WebSocket으로 브로드캐스팅하여 대화의 시작을 알림.
        • 향후 비동기 이벤트 처리로 전환 하여 성능 최적화를 계획.
      • Redis를 활용한 메시지 관리
        • Redis를 캐싱 레이어로 활용 하여 채팅 메시지의 실시간 저장과 빠른 조회 지원.
        • Redis 메시지를 주기적으로 DB에 동기화하여 데이터 손실을 방지하고 영속성 보장.
        • Redis와 DB 간 데이터를 병합하여 사용자가 시간순으로 정렬된 메시지를 확인할 수 있도록 처리.
      • 다양한 메시지 유형 처리
        • 일반 메시지와 System 메시지(안전거래 상황 등)를 구분하여 처리.
        • 거래 상황에 따른 메시지를 System 메시지로 생성하여 사용자에게 명확히 전달.
          • 예: "안전결제가 완료되었습니다.", "안전결제가 취소되었습니다."
      • 채팅방 목록 조회 및 관리
        • 사용자별 채팅방 활성 상태(PostAuthorActive, RequestorActive)를 기반으로 사용자별로 필터링 된 채팅방 목록 제공.
        • 채팅방을 나가거나 삭제할 경우 Redis 메시지를 DB로 동기화하고, Redis 메시지 데이터를 정리.
        • 양측 사용자가 모두 채팅방을 나가면 삭제 상태로 처리하여 관리.
      • 채팅방 나가기 시 사용자별 나간 시점 기록
        • @ElementCollection 을 사용해 사용자별 나간 시점을 관리 하며, 별도 테이블 없이 사용자별 데이터를 독립적으로 저장.
        • 사용자 나간 시점을 기록하여 읽지 않은 메시지 수를 정확히 계산 하는 데 활용.
        • 향후 클라이언트에서 나간 시점을 활용하여 사용자가 채팅방에서 나가기 전, 어디까지 메시지를 읽었는지 확인할 수 있는 기능 추가 예정.
  • ✨ 안전결제

    • 💡 헬스장 이용권은 금액대가 소액이 아니기 때문에 개인 간 거래에서 발생할 수 있는 금전 문제를 방지하기 위해 도입했습니다.
    • 💻 구현한 기능
      • 안전결제 요청 : 로그인 한 회원이 해당 채팅에 참여 중인지, 해당 게시글에 진행 중인 다른 안전결제가 있는지 검증합니다.검증에 통과하면 SafePayment 테이블에 저장하고 Message를 전송합니다.
      • 안전결제 승인 : 구매자의 GymPay에서 출금을 시도합니다. 만약 잔액이 부족하거나 GymPay를 개설하지 않았다면 예외를 던집니다.
      • 안전결제 거절 : 로그인 한 회원이 요청을 받은 사람인지 확인하고 해당 안전결제의 Status를 변경합니다.
      • 안전결제 취소 : 안전결제가 승인된 이후라서 구매자에게 출금이 진행되었다면 다시 구매자의 GymPay에 입금합니다.
      • 안전결제 확정 : 판매자에게 금액이 입금됩니다.
  • ✨ JWT를 이용한 사용자 인증

    • 💡 사용자의 인증 및 보안을 강화하기 위해 JWT를 활용하여 Stateless 인증 방식을 구현하였습니다. 해당 기능으로 클라이언트-서버 간의 효율적이고 신뢰성 높은 인증 체계를 제공하였습니다.
    • 💻 구현한 기능
      • JWT 토큰 생성 및 검증
        • JwtTokenProvider를 구현하여 JWT 토큰 생성, 검증, 클레임 추출 기능을 처리
        • 사용자 정보(email, ID, roles 등)를 포함한 JWT 생성
        • JWT의 만료 시간 설정 및 유효성 검증 처리
      • 사용자 인증 필터 구현
        • JwtAuthenticationFilter를 구현하여 클라이언트의 요청 헤더에서 JWT를 추출하고 인증 정보(SecurityContext)를 설정
        • 예외 상황 처리(유효하지 않은 토큰, 만료된 토큰 등)
      • Redis를 활용한 Refresh Token 관리
        • Refresh Token을 Redis에 저장하여 만료 시간 및 재사용 방지 기능 제공
        • Access Token 만료 시 Refresh Token을 이용하여 재발급 처리
      • 회원 로그인 및 로그아웃, 비밀번호 재설정 기능 등
        • 로그인 시 JWT Access Token 및 Refresh Token을 발급
        • Redis를 활용하여 Refresh Token의 유효성을 관리
        • 로그아웃 시 Refresh Token을 삭제하여 보안 강화
        • 인증된 사용자만 비밀번호를 재설정할 수 있도록 JWT와 기존 비밀번호를 활용한 검증 구현
      • JWT 기반 권한 부여
        • 사용자 권한(Role)을 토큰에 포함하고, 이를 활용하여 API 접근 제어
        • Spring Security와 연동하여 사용자 권한 관리
  • ✨ 이미지 업로드

    • 💡 회원, 게시글에 사진을 등록하기 위해 구현하게 되었습니다. 서버를 거치지 않고 클라이언트가 직접 이미지를 S3에 업로드 함으로써 서버의 부담을 줄일 수 있도록 했습니다.
    • 💻 구현한 기능
      • S3 Bucket에 접근할 수 있도록 Bucket 정보를 Config에 작성했습니다.
      • 인증 정보를 사용해 presigned url을 요청하는 메서드를 구현했습니다. 메서드 내부 로직은 아래와 같습니다.
        • Param으로 디렉토리 이름와 원본 파일명을 받아서 디렉토리 이름/연/월/일/UUID-원본파일명.확장자 형태로 업로드 파일명을 생성합니다.
        • 인증 정보, 파일명 등을 담아 AWS에 요청을 보냅니다.
        • 반환된 url을 클라이언트에 전달합니다.
  • ✨ 실시간 알림 기능

    • 💡 사용자에게 주요 알림(게시글 찜, 회원 정지상태 변경 등) 을 실시간 으로 전달하여, 빠르고 직관적인 정보 제공을 목표로 실시간 알림 기능을 구현하였습니다. 이를 통해 사용자는 웹 페이지를 새로고침 할 필요 없이, 주요 알림을 실시간으로 받아볼 수 있습니다.
    • 💻 구현한 기능
      • SSE (Server-Sent Events) 를 사용하여 구현 하였으며, 클라이언트와 서버 간의 연결을 유지하여 알림이 발생할 때마다 바로 전달됩니다.
      • 알림 목록 조회 및 알림 읽음 상태 변경 기능을 통해 사용자가 알림을 관리할 수 있도록 하였습니다.
      • 1분 마다 재연결 하는 로직을 통해 안정적인 연결을 유지할 수 있습니다.

‼️ 트러블 슈팅

  • 순환 의존성 문제

    • 💡 프로젝트에서 각 클래스 간 의존성이 증가하면서 서로의 의존관계가 복잡해졌습니다. 특히 A 클래스와 B 클래스가 서로를 참조하거나, 여러 서비스가 서로를 참조하며 호출하는 과정에서 의존성 사이클이 발생하였습니다.
    • ❓ 순환의존성이 발생하여 애플리케이션 구동 시 스프링 컨테이너가 빈을 생성할 때 서로의 빈 생성이 완료되지 않아 예외가 발생하며 애플리케이션 실행이 되지않았습니다.
    • 🔑
      • 데이터베이스 조회 로직을 별도의 QueryService 와 같은 중간 계층을 두어, 순환 의존성을 해소 하며, 공통 조회 로직은 해당 클래스에 위임하고, 기존 서비스간의 직접적인 의존성을 제거하였습니다.
      • 엔티티 간의 단방향 관계를 양방향 관계로 변경하여 필요한 데이터의 접근성을 개선하여, 순환 참조를 피하기 위해 필요할 때만 mappedBy 를 활용하여 의존관계를 해결하였습니다.
  • 지역선택 API 작동 중 에러

    • 💡 회원의 관심지역을 기준으로 데이터를 필터링할 수 있도록 지역 데이터를 카카오맵 기준으로 사전에 DB에 등록하였습니다. 지역 데이터는 상위지역(시 / 도)과 하위지역(구 / 군) 구조로 설계되었습니다.
    • ❓ 세종특별자치시와 같이 하위 지역이 없는 경우에도 동일한 데이터 구조를 요구하는 로직으로 인해 데이터가 누락되거나 API 호출이 실패하였습니다.
    • 🔑 하위 지역이 없는 경우에도 상위 지역 데이터를 일관되게 처리할 수 있도록 depth 값을 조정 하여 해결하였습니다.
      • 기존 상위 지역은 depth = 0
      • 하위 지역은 depth = 1
      • 하위 지역이 없는 경우에는 depth = 2 로 설정하여 구분
  • WebSocket 연결 시 JWT 인증 문제

    • 💡 사용자 인증을 위해 JWT(JSON Web Token) 기반 인증 방식을 활용했으며, WebSocket을 통한 실시간 채팅 기능을 제공하기 위해 WebSocket 초기 연결 시 JWT를 검증하는 로직이 필요했습니다. WebSocket은 HTTP 기반 프로토콜과 인증 방식이 다르기 때문에, Spring Security의 기존 인증 흐름과는 별개로 WebSocket 연결 시 JWT를 안전하고 효율적으로 처리할 방법을 설계 및 구현해야 했습니다.
      • WebSocket 연결과 STOMP 프로토콜의 한계
        • STOMP는 기본적으로 WebSocket 위에서 동작하며, HTTP 헤더를 포함하여 JWT를 처리하는 명시적인 표준이 부족했습니다.
        • WebSocket의 CONNECT 단계에서는 Spring Security의 기본 HTTP 필터 체인이 동작하지 않으므로 JWT 검증 로직을 따로 작성해야 했습니다.
      • JWT 처리 로직의 부재
        • Spring Security는 WebSocket과 STOMP 환경에서 JWT 인증을 자동으로 처리하지 않습니다.
        • WebSocket 연결 초기 단계에서 JWT를 검증하기 위한 직접적인 핸들러 또는 인터셉터 구현이 필요했습니다.
    • 🔑
      • 커스텀 StompHandler 구현
        • WebSocket 연결 시 StompHandler를 구현하여 CONNECT 메시지에서 Authorization 헤더를 읽고, JWT를 검증하는 로직을 작성했습니다.
        • 헤더에 포함된 JWT를 파싱하여 사용자 식별 정보를 추출하고, 이를 STOMP 세션에 저장하도록 구현했습니다.
      • STOMP 세션에 사용자 정보 저장
        • JWT 검증이 성공하면, STOMP 세션에 사용자 식별 정보를 저장하여, 이후 메시지 전송 시 인증 상태를 유지합니다.
        • 이로써 사용자 간의 실시간 채팅 메시지가 안전하게 송수신될 수 있었습니다.
  • CORS 문제

    • ❓ 백엔드 서버와 프론트 엔드 서버 연동 작업 중 header에 액세스 토큰을 담아 보내는 API 요청 시 CORS 에러가 발생했습니다.
    • 🔑
      • WebMvcConfigurer 에서 프론트에서 접근하는 urlhttp://localhost:5173/을 allowedOrigins로 설정해두었습니다.
      • 서버 측에서 Config 설정 이후에도 해결이 안 되는 경우, 클라이언트 측에서 프록시 서버를 설정하여 해결할 수 있다. 프록시 서버 우회는 개발 환경에서만 가능하므로 주의한다.
      • Proxy는 API로 네트워크 요청 / 응답을 주고 받는 클라이언트와 서버 사이를 중개하는 중간자 역할로 Proxy 설정을 한다면 요청을 날릴 때 Origin을 바꿔서 서버에 날릴 수 있기 때문에 next.js에서 제공해주는 rewrites를 사용해서 해결했습니다.
  • SPRINGBOOT SECURITY 설정문제

    • 💡 애플리케이션의 보안을 강화하기 위해 Spring Security를 사용하여 인증 및 권한 부여를 구현하였습니다. JWT를 활용하여 인증 과정을 처리하고, 필요한 엔드포인트별로 접근 제어를 설정하였습니다.
    • ❓ 인증이 필요 없는 엔드포인트(/api/auth/sign-up, /api/auth/sign-in 등)에서 요청이 차단되는 문제, CORS 관련 설정이 누락되어 프론트엔드와의 통신이 실패하는 문제
    • 🔑
      • CORS 설정 추가 : http.cors(Customizer.withDefaults())를 추가하여 기본적인 CORS 설정을 활성화하여 프론트엔드와의 통신에서 발생하던 차단 문제를 해결
      • 엔드포인트 인증 제외 처리
        • exemptUrls() 메서드에 인증이 필요 없는 엔드포인트를 명시적으로 추가
        • requestMatchers()를 사용하여 인증 제외 경로를 구체적으로 지정
      • RESTful API 방식으로 동작하므로 http.csrf().disable()를 사용하여 불필요한 CSRF 검사를 비활성화

About

💪 Go-Gym

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages