GPT 요약

My-Music-Note 프로젝트에서 SonarCloud와 GitHub Actions 같은 SaaS 도구를 활용해 서비스 비용을 절감했습니다.
Bastion Host 대신 AWS Systems Manager를 도입해 보안을 강화하고 네트워크 비용을 줄였습니다.
이러한 선택으로 비용 효율성과 운영 효율성을 모두 확보할 수 있었습니다.

My-Music-Note 프로젝트에서의 경험을 다룬 글입니다.


이전 프로젝트들은 교육기관이나 회사에서 제공한 환경에서 진행되었기 때문에 서버 비용 같은 것은 크게 신경 쓰지 않았습니다.

그러나 이번 프로젝트는 직접적으로 돈이 나가는 상황이었기 때문에 자연스럽게 비용을 줄이면서도 효율성을 높이는 방법에 대해 고민하게 되었습니다.

서비스 비용 절감 - SonarCloud와 GitHub Actions 선택

My-Music-Note 이전에 진행했던 My-Books 프로젝트에서는 SonarQube로 코드 품질을 관리하고 CI/CD 도구로는 Jenkins를 사용했습니다.
My-Books는 NHN Academy에서 진행한 것으로 인프라 관리비용을 NHN측에서 전액 부담해주었기 때문에 비용 고민이 없었습니다.

하지만 이번 프로젝트는 제 돈으로 운영해야 했기 때문에 별도의 서버 관리가 필요하지 않은 도구를 찾아야 했으며
결과적으로 SonarCloudGitHub Actions라는 SaaS 기반 도구를 선택하게 되었습니다.

SonarCloud - SaaS형 코드 품질 관리 도구

SonarCloud는 SaaS(Software as a Service)로 제공되며 SonarQube처럼 별도의 서버를 설치하거나 관리할 필요가 없습니다.GitHub 공개 저장소에 한해서 전체 기능을 무료로 제공받을 수 있어 규모가 작은 스타트업이나 팀 프로젝트의 경우에는 매우 적합하다 생각합니다.

물론 SonarQube에 비해 부족한 부분도 존재합니다.

  • 제한된 언어 지원 언어지원과(C,Objective-C,PL/SQL...)
  • 타사 플러그인의 부재
  • 모노레포등 복잡한 프로젝트 구조에 대한 지원 부족

다행히 My-Music-Note 서비스는는 이러한 제약 조건과 관련이 적었기 때문에 SonarCloud를 사용하는 데 만족했습니다.

SonarCloud Review

GitHub Actions - 클라우드 기반 CI/CD

GitHub Actions도 SaaS로 제공되며 SonarCloud와 동일하게 GitHub 공개 저장소에 한해서 무료로 사용할 수 있습니다.

Jenkins와 달리 서버 비용이 필요하지 않으며 GitHub Actions Marketplace 활용시 AWS, Google Cloud , Docker 등 다양한 Actions를 쉽게 추가하여 CI/CD 파이프라인을 확장할 수 있습니다.

개인적으로는 이 부분이 매우 강력하다고 느꼈으며 Jenkins와 비교해도 기능이 제한적일 것 같다는 생각은 들지 않았습니다.

특히나 국내 빅테크에서도 GitHub Actions를 사용하는 사례가 있는만큼 대규모 조직에서도 충분히 활용 가능해보입니다.

네트워크 비용 절감 - Bastion Host에서 Systems Manager로 전환

AWS 기반 인프라에서는 프라이빗 서브넷의 EC2 인스턴스에 접근하는 방법 또한 비용 절감 요소중 하나 입니다.
기존에는 Bastion Host를 사용했지만 이 방식에는 몇 가지 한계가 있었습니다.

Bastion Host - 작동 방식과 한계

Bastion Host는 퍼블릭 서브넷에 배치된 EC2 인스턴스로 외부에서 접근 가능한 경로를 제공합니다.
이를 통해 프라이빗 서브넷의 EC2 인스턴스에 접근할 수 있지만 다음과 같은 문제점이 발생합니다.

  1. 보안 취약점:
    • Bastion Host는 외부 인터넷에 노출되어 보안 위협이 존재
  2. PEM 키 관리의 복잡성:
    • 여러 프라이빗 인스턴스의 PEM 키를 Bastion Host에 복사하고 관리해야 함
  3. 추가 비용 발생:
    • Bastion Host 자체가 EC2 인스턴스이기 때문에 유지비용이 발생

Bastion Host 목적의 추가적인 EC2 자원 필요

Systems Manager - 더 안전하고 효율적인 네트워크 접근 방식

이러한 문제를 해결하기 위해 AWS Systems Manager를 도입했습니다.

Systems Manager는 AWS 내부 네트워크를 활용하기 때문에 인터넷 연결 없이도 EC2 인스턴스에 안전하게 접근할 수 있으며
PEM 키를 관리하지 않아도 되는 편리함 , Bastion Host와 같은 EC2 인스턴스를 유지할 필요가 없어 비용이 절감되는 등 다양한 장점이 존재합니다.

실제로 사용해보니 일반적인 터미널 환경과 유사했으며 기존 Bastion Host 방식보다 간편하고 효율적이였습니다.

SSM을 이용한 Private Subnet EC2 접속

My-Music-Note 프로젝트에서 SaaS 기반 도구(SonarCloud, GitHub Actions)와 Systems Manager를 도입한 결과 서비스 비용과 네트워크 비용을 모두 절감할 수 있었습니다.

같이 보시면 좋은 발표 영상입니다.

GPT 요약

My-Music-Note 프로젝트에서 AWS 기반 인프라를 구축하며 고가용성 아키텍처를 설계하고, ASG와 ALB로 트래픽 분산을 구현했습니다.
CodeDeploy와 Docker를 결합해 배포 프로세스를 자동화했으며, Docker 이미지를 활용한 컨테이너 기반 환경으로 전환하여 개발 생산성과 효율성을 높였습니다.
이 경험을 바탕으로 SAA 자격증을 취득하며 AWS와 컨테이너 기술에 대한 깊은 이해를 확립했습니다.

My-Music-Note 프로젝트에서의 경험을 다룬 글입니다.

My-Music-Note 프로젝트에서 유일한 백엔드 개발자로서 AWS 기반 인프라 구축에 집중하였습니다.
처음 AWS를 사용하며 겪었던 어려움과 SAA(Solutions Architect Associate) 자격증 취득, 실제 운영 환경에서의 개선 과정을 공유하고자 합니다.

AWS 첫 경험과 비용 관리

이전에 진행한 My-Books 프로젝트에서는 주로 API 기능 구현과 인증/인가 프로세스 구축을 담당했기 때문에인프라 설계 및 구현 경험은 부족했습니다. 따라서.. My-Music-Note 프로젝트에서는 이러한 부분을 보완하고자 AWS를 활용하여 인프라를 구축하였습니다.

AWS 프리 티어(Free Tier)를 활용할 수도 있었지만 실제 과금 모델을 경험하는 것이 중요하다고 판단하여 유료 서비스를 사용했습니다.
실제로 주머니에서 돈이 빠져나가는 경험을 하니 어떻게하면 비용을 줄일 수 있을까? 고민 하게 됐으며
NAT 게이트웨이, 퍼블릭 IP, ALB(Application Load Balancer) 등의 예상치 못한 비용에 대해서도 학습할 수 있었습니다. 

새로운 목표 - 고가용성 아키텍처 구축

프로젝트를 시작할때 목표로 잡은것은 고가용성 아키텍처를 구축하는 것이었습니다.
고가용성은 서비스가 지속적이고 안정적으로 운영될 수 있는 능력을 의미하며 이는 모던 아키텍처에서 필수적인 요소라 생각합니다.

따라서 다음의 기준을 세우게 됩니다.

  1. 트래픽을 자동으로 분산시킬 수 있을 것
  2. 장애 감지 시 새로운 리소스를 자동으로 생성하고 교체할 것
  3. 무중단 배포가 가능할 것

트래픽 분산 - ASG와 ALB

트래픽 분산을 위해 AWS의 ELB(Elastic Load Balancer)를 조사하면서 공식 문서를 참고해 ASG(Auto Scaling Group)와 연계하여 사용하는 방법을 학습했습니다.

Elastic Load Balancing 로드 밸런서를 Auto Scaling 그룹에 연결 - Amazon EC2 Auto Scaling
이 문서를 통해 ASG와 ELB의 연계로 트래픽을 효율적으로 처리하는 방법을 이해할 수 있었습니다.

ASG와 ELB를 연계해 사용하는 이유는 간단합니다. ASG를 통해 새로운 인스턴스를 생성하더라도 요청이 해당 인스턴스로 분배되지 않으면 무용지물이기 때문입니다.

ASG는 트래픽 증가나 장애 발생 시 인스턴스 수를 동적으로 조절하여 확장성가용성을 높여줍니다. 또한, 대상 추정 정책(Target Tracking Policy)을 사용하면 CPU 사용률과 같은 지표를 기반으로 자동 조정이 가능합니다.

ASG(Auto Scaling Group)

ELB는 생성된 인스턴스로 트래픽을 효율적으로 분배하며, 이를 통해 트래픽 부하 분산과 확장 작업이 자동화됩니다.

ELB에는 ALB(Application Load Balancer)와 NLB(Network Load Balancer)가 존재하지만 My-Music-Note는 REST API 기반의 웹 애플리케이션이므로 HTTP/HTTPS 트래픽 처리를 지원하는 ALB를 사용했습니다.

무중단 배포와 자동화 - CodeDeploy의 도입

트래픽을 분산시키고 ASG로 새로운 리소스(EC2 Instance)를 생성했지만 한 가지 문제가 있었습니다.
"배포는 어떻게 하지?"라는 고민이 생긴 것이죠.

기존의 ASG와 ALB 스택에 연계할 수 있는 배포 도구를 조사하던 중 AWS의 CodeDeploy를 도입하게 되었습니다.

EC2/온프레미스 컴퓨팅 플랫폼의 배포 - AWS CodeDeploy
CodeDeploy를 통해 무중단 배포와 자동화된 배포 프로세스를 설정할 수 있었습니다.

CodeDeploy를 활용하려면 S3에 업로드된 아티팩트AppSpec 파일이 필요합니다.
이를 위해 초기에는 소스 코드와 Gradle 빌드 산출물(JAR), 설정 파일, 배포 스크립트를 모두 압축하여 S3에 업로드하는 방식을 사용했습니다.

초기 배포 프로세스

하지만.. 배포 과정에서 새로운 EC2 인스턴스가 프로비저닝되었지만 애플리케이션이 설치되지 않는 문제가 발생했습니다.
이는 CodeDeploy 에이전트가 EC2 인스턴스에 설치되어 있지 않아서 발생한 문제였습니다.

이를 해결하기 위해 EC2 템플릿의 사용자 데이터(User Data)를 활용했습니다. 사용자 데이터는 EC2 인스턴스가 처음 시작될 때 실행되므로, CodeDeploy 에이전트를 자동으로 설치하고 배포 프로세스를 완성할 수 있었습니다.

배포 완료


문제점 - 비효율적인 zip파일 & 환경 구성의 어려움

그러나 또 다른 문제가 있었습니다. 프론트엔드 개발자가 백엔드 개발 환경을 구성하는 과정이 복잡했습니다.
AWS에 로그인해서, S3에 접근해서, zip파일 다운받고 jar실행시키고.. JRE도 있어야하고.. 이는 많은 번거로움을 주었습니다.

또한 S3에 필요한 소스코드,빌드결과물 등을 담은 zip파일이 배포마다 생기는 것 또한 비효율적으로 느껴졌습니다.

컨테이너 기반 환경으로의 전환

이 문제를 해결하기 위해 Container 기반 애플리케이션으로 마이그레이션하기로 했습니다.
컨테이너화를 위해 Docker를 사용하고, EC2 인스턴스에는 Docker가 설치된 상태로 프로비저닝되도록 설정했습니다.

CodeDeploy 에이전트의 경우 크기가 크지 않았기 때문에 사용자 데이터를 기반으로 설치했지만 모든 배포마다 Docker 설치를 반복하는 방식은 부담스럽게 느껴졌기 때문에 CodeDeploy와 Docker를 설치한 AMI를 만들어 사용해주었습니다.


배포 프로세스또한 개선했습니다.

  • S3에 zip 파일을 업로드하던 방식 -> Docker 이미지를 Docker Hub에 올리고 EC2 인스턴스에서 이를 실행
  • AppSpec.yml과 스크립트를 포함한 deployment.zip 파일을 미리 준비해 배포 과정에서 재활용

이 과정을 통해 프론트엔드 개발자는 단순히 Docker와 PostMan을 활용해 백엔드 환경을 손쉽게 구성할 수 있게 되었으며
배포마다 생성됐던 zip파일을 최소화 할 수 있었습니다.

프로젝트 당시 Notion 문서

완성된 아키텍처와 SAA 자격증 취득

완성된 아키텍처는 다음과 같습니다.

  • ASG와 ALB를 활용한 트래픽 분산
  • CodeDeploy와 Docker를 결합한 효율적인 배포 프로세스
  • NAT Gateway를 통한 Private Subnet의 네트워크 연결

My-Music-Note 프로젝트는 익숙하지 않았던 AWS와 컨테이너 기술에 몰입할 수 있었던 프로젝트였습니다.
또한 배운 것을 검증하고 더욱 Deep Dive하기위해 AWS Certified Solutions Architect - Associate 자격증 취득했으며
백엔드 개발자로서 역량을 키울 수 있었습니다.

AWS Solutions Architect Associate(SAA)합격 후기


같이 보시면 좋은 발표 영상입니다.

 

GPT 요약

My-Books 프로젝트에서 로그인 절차를 설계하며, 평문 비밀번호 전송 문제를 해결하기 위해 고민했습니다.
기존에 Back Server에서 비밀번호를 암호화하던 방식을 Front Server에서 해싱 후 전달하는 방식으로 변경해 보안성을 강화했습니다.
로그인 요청은 호출 빈도가 낮아 추가적인 서버 호출 비용보다 보안성을 우선시하는 선택이 이루어졌습니다.

My-Books 프로젝트에서의 경험을 다룬 글입니다.

My-Books 프로젝트에서 제가 맡은 역할은 인증/인가 프로세스를 설계하고 구현하는 것이었습니다.
특히 로그인 절차(인증)를 구현하면서 가장 큰 고민은 평문과 비문의 사용과 이를 어떻게 안전하게 처리할 것인가에 대한 것이었습니다.
이번 글에서는 이러한 고민의 과정과 최종적으로 내린 선택에 대해 공유하려 합니다.

BCrypt를 어디에 사용할 것인가?

로그인 설계에서 가장 중요한 고민 중 하나는 BCrypt를 어디에서 사용할 것인가 하는 점이었습니다.

초기 설계

초기에는 프론트에서 평문 비밀번호를 받아 이를 백으로 넘긴 후 BCrypt로 암호화하고, 데이터베이스와 비교해 검증하는 방식으로 설계했습니다.
이는 일반적으로 많이 사용하는 방법으로, 간단하고 직관적이라는 장점이 있었습니다.

최종 설계

하지만 최종적으로는 프론트에서 사용자가 입력한 이메일을 백으로 전달해 검증하고 DB에 저장돼있는 비문과 입력한 비밀번호를 BCrypt를 통해 검증한 뒤 로그인 절차를 완료하는 방법을 선택했습니다.
이로 인해 로그인 절차는 다소 복잡해졌고, 백서버 호출도 기존 1번에서 2번으로 증가하게 되었습니다.

BCrypt를 사용하기 위한 Dependency 변경

왜 변경했을까?

이건 정말 단순한 이유인데요, 저는 평문 비밀번호가 통신 과정에서 평문으로 전송되는 것 자체가 너무 찝찝했습니다.
물론 HTTPS를 사용하면 암호화된 통신을 보장할 수 있지만 비밀번호 같은 민감한 데이터는 단 한 번이라도 평문으로 노출될 가능성을 없애는 게 중요하다고 생각했습니다.

또한 로그인 요청은 사용자가 자주 호출하지 않는 기능입니다.
대부분 사용자가 서비스를 시작하거나, 토큰이 만료된 후 로그인을 수행하기 때문에 호출 빈도가 비교적 낮습니다.
따라서 로그인 절차에서 발생하는 추가적인 서버 호출 비용은 최소화된 부담이라고 판단했습니다.

비용 최적화를 추구할 수도 있었지만, 보안성을 강화하는 선택이 사용자의 민감한 정보를 다루는 인증 시스템의목표와 일치한다고 결론 내렸습니다

BCrypt 값 검증 방식 

또한 다음과 같은 질문도 받게 되었는데요 
Q: 프론트,백 둘 다 BCrypt를 사용하고 프론트에서 암호화 한 후 전송한다음에 백에서 확인하면 한번으로 처리가능한거 아니에요? 

A: BCrypt를 이용해 값의 일치를 확인하려면 평문하나와 비문 하나가 필요해서 안될것 같아요 

이 것을 이해하려면 BCrypt의 동작 구조를 알아야 하는데요 BCrypt는 단방향 해쉬 알고리즘으로 한번 암호화하면 본문을 알 수 없게됩니다. 
그런데... 원문을 알 수 없는데 어떻게 입력한 비밀번호와 DB의 암호화된 비밀번호가 같다는 것을 알 수 있을까요??

정답은 간단하게도 다시 암호화를 시킨후 비교합니다.

Bcrypt는 내부적으로 Salt(임의의 값)를 생성하여 평문 비밀번호와 함께 해싱 알고리즘을 적용합니다.
생성된 Salt는 해싱 결과(비문)에 포함되며, 검증 시 동일한 Salt를 추출해 평문 비밀번호와 함께 다시 해싱합니다.
이렇게 생성된 비문이 기존 비문과 같다면, 암호화되기 전의 평문이 동일했음을 확인할 수 있습니다.

따라서.. 들어온 평문과 암호화된 비문이 같음을 알기 위해서는 무조건 평문 하나와 비문 하나가 필요하기 때문에 
백서버에서 비문 비밀번호를 응답으로 받은 후 프론트에서 들어온 평문을 이용해 검증하는 방식을 사용해야 합니다.

My-Books 로그인 절차

최종적으로 설계된 My-Books 로그인 절차는 다음과 같은 흐름으로 진행됩니다.

  1. 이메일과 비밀번호 입력
    • 사용자가 이메일과 비밀번호를 입력하여 로그인을 시도합니다. 이 정보는 Front Server를 통해 Gateway Server로 전달
  2. 이메일 인증 요청
    • Gateway Server는 Back Server로 이메일 인증 요청을 전송
    • 이메일 인증 실패 시: 로그인 절차가 중단되고, 인증 실패 응답이 반환
    • 이메일 인증 성공 시: Back Server는 암호화된 비밀번호(BCrypt로 해싱된 값)를 Gateway Server를 통해 Front Server로 응답
  3. 비밀번호 검증
    • 사용자가 입력한 평문 비밀번호와 Back Server에서 전달받은 암호화된 비밀번호를 비교하여 검증
    • 비밀번호 검증 실패 시: 인증 실패 응답이 반환
    • 비밀번호 검증 성공 시: 로그인 절차 진행 , 마지막 로그인 시간 갱신 및 포인트 적립 요청
  4. 토큰 발급
    • 토큰 발급 후 로그인처리 완료

시퀀스 다이어그램을 통해서 보면 다음과 같습니다.(Resource Server는 Back Server와 동일)

로그인 시퀀스 다이어그램

추가적으로 같이 보시면 좋은 발표 영상입니다.

들어가는 글

안녕하세요 12월 30일 AWS SAA자격증을 취득했습니다.

정보처리기사는 필요에 의해서 취득한 느낌이 강했지만 SAA의 경우는 실제로 AWS를 사용해보면서 얻은 지식을 검증하기위해 취득한 것으로 준비하는 과정이 재밌었던 것 같습니다.

특히나 사용해보지 않았던 서비스들을 간략하게 이해할 수 있었고 서버리스 아키텍처에도 흥미가 생겨 다음에 진행할 사이드 프로젝트는 API Gateway & Lambda를 써볼까 생각하고 있습니다.

공부시간/느낀점


저의 경우 프로젝트를 진행하면서 꾸준히 AWS에 대해 학습했기 때문에 공부 시간을 선정한다는 것이 조금 애매하지만
SAA취득만을 목표로 공부한 시간은 이틀정도 되는것 같습니다.

EC2, SG , NAT , VPC , Subnet , ASG , ELB , S3 , CloudFront 등에 대해서 사전 지식이 있다면 
온 프레미스 환경에서 AWS기반으로 Migration하는 부분만 보시면 되기 때문에 많은 시간이 필요해 보이진 않습니다.

그런만큼 SAA를 취득했다고 해서 "AWS에 대해 높은 이해도를 가진다!" 라고 말하기도 어려우며 사실상 Spring과 비슷한 느낌인 것 같습니다.

백엔드 누구나 Spring을 사용하지만 Spring Core , MVC , WebFlux , Cache , Security , Data .... 등 너무나 방대한 프레임워크이기 때문에 "나는 Spring을 잘 다룬다!"라고 말할 수 있는 사람이 많지 않듯이요 

이 글은 2024년에 취득과 동시에 작성하려했는데요.. 자격증 시험날 자체가 12월 30일이기도 했고 해커톤 일정과 겹쳐 2025년에 글을 작성하고 있습니다.

아무래도 날이 추워지면서 몸이 굳어 게을러지기라도 하는 모양입니다.

마지막으로 AWS관련 발표와 합격사진 올리고 마무리하겠습니다.
감사합니다.



 

'자격증' 카테고리의 다른 글

정보처리기사 합격 후기  (3) 2024.12.18

GPT 요약

My-Books 프로젝트에서는 Spring Gateway를 활용해 URL 기반 라우팅과 권한별 필터로 인가 로직을 설계했으나, 코드 중복과 비효율 문제가 발생했습니다.
SonarQube 피드백을 기반으로 중복 코드 문제를 해결하기 위해 유저와 어드민 필터를 통합하여 AuthFilter로 최적화했습니다.
사용자 상태 검토와 권한 검증을 효율적으로 처리하면서도 유지보수성과 확장성을 확보한 인가 시스템을 완성했습니다.

My-Books 프로젝트에서의 경험을 다룬 글입니다.

My-Books 프로젝트에서는 인증/인가 시스템 설계가 사용자 경험과 서비스 성능에 직결된다는 점을 알게 되었습니다.
이번 글에서는 Spring Gateway를 활용해 인가 로직을 최적화한 과정을 공유합니다.

초기 설계 - 단순 URL 기반 라우팅

처음에는 인증 서버와 백엔드 서버의 URL을 기반으로 라우팅을 구현했습니다.

  • 인증 서버: /auth/**
  • 백엔드 서버: /api/**

이 방식은 기본적인 요청 분리는 가능했지만, 모든 요청에 대해 인가 처리가 일어나는 비효율적인 구조였습니다.

이는 서비스 성능에 부정적인 영향을 끼쳤고, 이를 개선하기 위해 권한별 라우팅을 도입하기로 했습니다.

인가 처리 - 권한별 요청 구분

Spring Gateway의 RouteLocator를 활용해 요청을 권한별로 분리했습니다

  • 권한이 필요 없는 요청: /api/** , /auth/**
  • 유저 권한이 필요한 요청: /api/member/**
  • 어드민 권한이 필요한 요청: /api/admin/**

이제 권한이 필요 없는 요청은 인가 로직을 생략할 수 있어, 불필요한 리소스 낭비를 줄일 수 있었습니다.
또한 권한별 인가처리 세분화를 위해 유저 필터와 어드민 필터를 각각 구현했습니다.

  • 어드민 필터
    • 어드민 권한 확인.
    • 토큰의 유효성, 만료 여부, 조작 가능성 검토.
    • 요청 URL을 백엔드 서버의 REST API 형식에 맞게 변환.

  • 유저 필터
    • 유저 권한 확인.
    • 토큰의 유효성, 만료 여부, 조작 가능성 검토.
    • 요청 URL을 백엔드 서버의 REST API 형식에 맞게 변환.

다음과 같이 백엔드 API URL을 설정하는것도 생각해봤지만

  • /api/admin/**
  • /api/member/**

REST API형식에 어긋난다 생각하였기에 Gateway Filter에서 URL형식을 변경하는 방식을 선택했습니다.
또한 권한 계층을 두어 어드민 권한을 갖고 있을 시 모든 유저 기능을 사용 가능하게 구현하였습니다.

유저 상태 검토 - 왜 필요했을까?

권한에 따라 요청을 분리하고 인가 로직을 적용하던 중, 사용자 권한 외에도 사용자의 상태에 대해서도 고민하게 됐습니다.

이런 경험이 있지 않은가요?
오랜만에 사이트에 접속해 로그인을 하니 휴면계정으로 보호되어있다거나.. 로그인 시도가 계속시도되어 계정이 잠겼다던가..

저 또한 이런 경험이 있었기에 권한 뿐 아니라 상태에 대해서도 검증이 필요하다 생각했으며 이를 위해 유저 상태 검토 로직을 필터에 추가했습니다.

adminAuthFilter
userAuthFilter

그런데 Filter를 완성하고 나니 다음과 같은 피드백을 받게됩니다.

Q: SonarQube보니까 코드 중복도가 엄청 높던데 이거 통합하면 안되나요?

A: 역할이 다르기 때문에 분리해놓은 것이라 통합할 생각 없는데요?

그런데... 정말 그런가요??

중복되는 로직 개선 - 필터 통합

초기 설계에서는 userAuthFilteradminAuthFilter를 분리한 이유가 있었습니다.
권한별로 역할을 분리해두면 코드가 명확해지고, 확장성을 유지할 수 있다고 생각했기 때문입니다.

하지만, SonarQube를 활용해 코드 중복도를 분석한 결과 두 필터 간 중복 코드가 상당히 많았고, 이로 인해 코드 품질이 저하된다는 피드백을 받았습니다.

필터를 통합하는건 어렵진 않았습니다. 하지만 , "서로 역할이 달라보이는데 중복이 많다는 이유로 통합해야할까?" 라는 고민에 빠졌습니다.

역할별로 타입을 나누어 구현하는 가장 큰 이유는 확장성과 유지 보수성에 있다고 생각합니다.
하나의 객체가 다양한 역할을 수행하면 코드가 복잡해지고 확장성이 떨어지니까요, 그런데 이 경우는 어떤가요?

  • "인가 처리 로직이 자주 변경될 가능성이 있을까?"
  • "유저나 어드민 외에 새로운 권한이 추가될 가능성이 있을까?"
  • "활성, 잠금, 휴면 외에 추가적인 상태가 필요한 시나리오가 있을까?"

이 질문들을 깊이 고민해본 결과, 인가 처리 로직이 자주 변경될 가능성이 낮으며, 새로운 권한이나 상태가 추가될 가능성 또한 희박하다고 판단했습니다.

따라서, 유저와 어드민 필터를 하나의 필터로 통합해도 충분히 안정적이고 효율적인 구조를 유지할 수 있다는 결론에 도달했습니다.

그렇게 통합된 AuthFilter 코드입니다.

항상 느끼는 것이지만 여러 사람의 의견을 듣고 그 의견들을 깊이 고민하는 과정이 좋은 결과물을 만드는 것 같습니다.
이렇게 Gateway 인가 로직을 최적화하는 여정은 마무리됩니다.

같이 보시면 좋은 발표 영상입니다.

GPT 요약

My-Books는 웹 환경을 고려해 JWT 만료기간을 재설계했습니다.
리프래시 토큰을 3일에서 1시간으로 축소해 보안을 강화했습니다.
서비스 특성에 맞춘 유연한 설정이 중요합니다.

My-Books 프로젝트에서의 경험을 다룬 글입니다.

My-Books 프로젝트에서 제가 맡은 역할은 인증/인가 프로세스를 설계하고 구현하는 것이었습니다.
이번 글에서는 JWT 만료기간에 대해 고민했던 계기와, 기존 정책에서 변경된 정책까지의 과정을 공유하려 합니다.

JWT 만료기간의 중요성

My-Books 프로젝트를 설계하면서 깊게 고민한 부분 중 하나는 토큰의 만료기간 설정이었습니다.

JWT는 클라이언트와 서버 간 인증 정보를 주고받을 때 사용되는데, 만료기간 설정이 짧을수록 보안이 강화되는 반면, 사용자는 더 자주 로그인을 해야 하는 불편함을 겪게 됩니다.

따라서 보안과 사용자 경험 간의 Trade-Off를 적절히 설정하는 게 중요합니다.

기존의 JWT 만료기간

기존의 JWT 만료기간입니다.

  • 액세스 토큰 30분
  • 리프래시 토큰 3일

(Secret의 경우 Key Manager가 관리하는 값이기 때문에 걱정안하셔도 됩니다.)

My-Books 프로젝트 초기에 리프래시 토큰의 만료기간을 3일로 설정했을 때는 문제가 없다고 생각했습니다.

30분짜리 액세스 토큰이 만료되더라도 리프래시 토큰을 통해 새 액세스 토큰을 발급받는 방식은 흔히 사용하는 방법이었기 때문입니다.

하지만, PC방에서 My-Books 서비스에 접속해 친구에게 자랑하던 중 한 가지 생각이 스쳤습니다.
"내가 로그아웃하지 않고 그냥 나가면 계속 로그인된 상태로 유지되겠네?"

더욱이, My-Books에서는 액세스 토큰이 재발급될 때 리프래시 토큰도 함께 재발급되어 만료기간이 재설정되는 구조였기 때문에, 이 문제가 더욱 심각하게 느껴졌습니다.

만약 로그아웃하지 않은 상태로 공용 PC를 떠나면, 다음 사용자가 내 계정을 그대로 사용할 가능성이 존재했던 것입니다.

이 경험을 통해, 웹 환경에서 공용 PC(도서관, 학교, 공항 등) 사용 가능성이 크다는 점과 사용자가 로그아웃하지 않거나 토큰이 노출될 경우 심각한 보안 문제가 발생할 수 있음을 인지하게 되었습니다.

따라서.. 사용자 경험이 다소 저하되더라도 리프래시 토큰 만료기간을 축소해야 한다는 결론에 도달하게 되었습니다.

도메인을 생각한 JWT 만료기간 재설정

다음은 변경된 JWT 만료기간입니다.

  • 액세스 토큰 30분 (변경 없음)
  • 리프래시 토큰 1시간 (기존 3일에서 대폭 축소)

(Secret의 경우 Key Manager가 관리하는 값이기 때문에 걱정안하셔도 됩니다.)

"3일에서 1시간이라니, 너무 짧은 거 아닌가?"라는 생각이 들 수 있습니다. 하지만 이 결정은 도메인의 특성을 고려한 조치입니다.

쿠팡과 같은 종합몰 서비스의 체류 시간에 비해 서점과 같은 전문몰의 경우 체류 시간이 상대적으로 짧게 측정됩니다.

또한 대부분의 사용자가 어떤 책을 구매할 것인지를 특정 짓고 방문하며, 책의 구매가 빈번하지 않다고 판단했기 때문에 1시간 정도의 만료기간도 충분할 것으로 판단했습니다.

모바일 환경이였다면?

만약 My-Books가 모바일 환경을 기반으로 설계되었다면, 만료기간을 더 길게 설정했을 것입니다.
대부분의 사람들은 개인적으로 핸드폰을 사용하기 때문에, 공용으로 사용될 가능성이 매우 낮습니다.

카카오톡은 모바일 환경에서 매우 긴 만료시간을 설정하여 오랜 시간 로그인을 유지합니다.
덕분에 우리는 몇 일 동안 카카오톡에 접속하지 않아도 로그인 요청을 받지 않는 것이죠.

이처럼 서비스의 사용 환경과 특성에 따라 JWT 만료기간은 크게 달라질 수 있습니다.

이와 관련해 추가적인 링크 남기면서 마무리 하겠습니다.

 

GPT 요약

My-Books 프로젝트에서 초기 JWT 방식의 보안 취약점을 개선하기 위해 리프래시 토큰을 Redis에 저장하고 IP와 User-Agent 정보를 활용해 보안성을 강화했습니다.
토큰에는 UUID를 사용하고 Redis에 사용자 정보를 매핑해 추가적인 보안을 구현했습니다.
이를 통해 확장성과 보안성을 모두 충족하는 인증/인가 시스템을 설계했습니다.

My-Books 프로젝트에서의 경험을 다룬 글입니다.

My-Books 프로젝트에서 저의 주된 역할은 인증/인가 프로세스를 설계하고 구현하는 것이었습니다.
이번 글에는 JWT 안정성을 개선한 과정에 대해서 설명하려 합니다.

JWT의 안전성

이전 포스팅 'JWT는 왜 사용할까?'의 마지막 부분에서 다음과 같은 언급을 한 적이 있습니다.

 

JWT는 왜 사용할까?

GPT 요약My-Books 프로젝트에서 세션 불일치 문제를 해결하기 위해 JWT 인증을 도입했습니다.JWT는 서버 자원 없이 확장성이 높고 MSA에 적합합니다.사용 시 보안에 유의해야 합니다.My-Books 프로젝트

masiljangajji-coding.tistory.com

이번 글에는 JWT 안정성을 개선한 과정에 대해서 얘기하려 합니다.

초기의 JWT 사용

초기의 방법은 엑세스 토큰만 운용하는 방식이었습니다.
엑세스 토큰을 운용하여 토큰의 유효기간에 가까워 진다면(5~10분 이내) 엑세스 토큰을 refresh하는 요청을 주어 갱신해주는 것이죠

하지만 클라이언트가 쿠키로 내려진 토큰을 탈취당하는 경우에 이 방법은 보안이 매우 취약해집니다.
(공격자가 계속해서 토큰을 갱신하며 사용가능하기 때문에)

또한 매 요청마다 유효기간을 확인해 줘야 하기 때문에 불필요한 추가 연산이 필요해집니다.
이러한 방법을 해결하고자 다음과 같은 과정을 거치게됩니다.

JWT 안전성 개선 - 쿠키

처음에는 쿠키의 탈취를 어렵게 만드는 방법을 우선적으로 고려했습니다. 쿠키는 다양한 보안 옵션을 설정할 수 있으며, 제가 사용한 옵션은 다음과 같습니다.

  1. Secure - HTTPS에서만 쿠키전송
  2. HttpOnly - JavaScript로 쿠키 접근 불가 , XSS 방어
  3. SameSite - CSRF 방어를 위한 정책

이를 통해 클라이언트-서버 간 통신에서 쿠키 탈취를 어렵게 만들었습니다.
그러나 쿠키가 탈취된 이후에는 여전히 대응할 방법이 부족하다는 문제가 남았습니다.

JWT 안전성 개선 - 리프래시 토큰

여러 문서를 찾아본 결과 가장 많이 사용되는 방법이 액세스 토큰과 리프래시 토큰을 사용하는 것이었습니다.

짧은 유효기간을 가진 액세스 토큰과 긴 유효기간을 가진 리프래시 토큰을 두어 액세스 토큰 탈취 시에도 짧은 유효기간으로 인해 공격을 막을 수 있다는 것입니다.

또한 크게 2가지 운용방식이 존재했습니다.

  1. 액세스 토큰과 리프래시 토큰을 모두 쿠키로 내려주기
  2. 액세스 토큰은 쿠키로 리프래시 토큰은 Redis와 같은 별도의 서버 자원에 저장

액세스 토큰과 리프래시 토큰을 모두 쿠키로 내려주기의 경우에 서버의 자원을 사용하지 않기 때문에 JWT의 Stateless 한 특성을 잘 살릴 수 있지만 액세스 토큰(쿠키)가 탈취당한 경우를 가정한다면 똑같이 쿠키로 저장돼있는 리프래시 토큰은 멀쩡할까?라는 의구심이 들었습니다.

액세스 토큰은 쿠키로 리프래시 토큰은 Redis와 같은 별도의 서버에 저장의 경우에 탈취가 어려워진다는 장점이 있지만 서버에 데이터가 저장됨으로 Stateful 해진 다는 단점이 존재합니다.

저의 경우 액세스 토큰은 쿠키로 리프래시 토큰은 Redis와 같은 별도의 서버에 저장방법을 선택했는데요

  1. 부분적으로 Stateful 해지는 것은 맞으나 Stateless의 이점을 전부 사용 가능함
  2. Redis에 리프래시 토큰을 저장하고 부르는 로직은 자주 변경 및 호출되지 않는다
  3. JWT 일반 문자열이기 때문에 매우 많은 사용자를 커버할 수 있다

MSA 환경이나 Scale-Out으로 구성된 서버에서 확장성을 보장하는 것이 JWT를 사용하는 가장 큰 이유라 생각합니다.

리프래시 토큰을 각각의 서버에 저장시킨다면 이는 확장성에 제약이 생길 수 있지만 목적이 아예 분리된 Redis 서버 사용 시 Front와 Back 서버에 확장에 영향을 주지 않을 것이라 생각했습니다.

또한 리프래시 토큰을 저장하고 부르는 로직은 최초 로그인 요청 혹은 액세스 토큰은 유효하지만 유효기간이 만료돼 갱신하는 경우만 존재합니다.
이는 자주 변경되거나 호출될 일이 적음을 의미하고 별도의 서버를 둠으로써 버그 발생의 위험이나 복잡도가 크게 증가한다고 생각하지 않았습니다.

마지막으로 JWT는 결국 문자열 덩어리기 때문에 큰 자원 소모 없이 많은 사용자를 커버할 수 있다고 생각했습니다.

그렇게 해서 개선된 코드는 다음과 같습니다.

Redis에 저장된 리프래시 토큰을 액세스 토큰만으로 접근이 가능하다면 문제가 생길 수 있기 때문에 IP주소를 추가로 조합해 주었습니다.
또한 액세스 토큰과 리프래시 토큰을 일회용으로 사용하게끔 만들어 보안성을 높이려 했습니다.

RemoteAddr 이슈

그런데.. 이상한 일이 발생했습니다.
왜.. Redis에 저장되는 IP 주솟값이 다 똑같지?, 심지어 내 IP 주소도 아니잖아?

로그를 남겨 IP와 관련된 모든 값을 확인해 봤습니다.
그럼에도 문제점을 파악하지 못했기 때문에 Front 서버에서 직접 헤더에 정보를 넣어 보내줬습니다.

여기서 알게된 것은 사용자의 요청이 Front 서버를 거쳐 Auth 서버로 넘어오는 것이기 때문에
실제로 확인되는 IP 정보는 Front 서버의 IP라는 것입니다.

따라서 Front 서버에서 직접 IP 주소와 User-Agent 정보를 DTO 형태로 넘겨주게 변경했습니다.
(IP 주소뿐 아니라 User-Agent 정보를 이용해 보안성을 더욱 높였습니다.)

JWT 안전성 개선 - PayLoad

그런데 코드 리뷰 중 다음의 피드백을 받게 됩니다.

지금 user_id를 그대로 토큰에 기입하고 있는데 이거 그냥 보여줘도 돼?

처음에는 사용자에게 쿠키를 내려주는 것이기 때문에 디코딩 해서 내용을 까 볼 사람이 있을까? 정도로 생각했었지만 더 깊게 생각해 보니 이 정보를 기반으로 사용자의 정보나 API의 형태를 유추 가능해짐을 알았습니다.

또한 user_id 정보를 굳이 알려줄 이유도 없기 때문에 토큰에는 UUID를 기입하고 Redis에 user_id정보를 넣어줬습니다.

Key : UUID+IP+User-Agent , Value : user_id

최종적으로 구성된 코드입니다.

UUID를 기반으로 새로운 키를 생성해 Redis에 user_id를 기입하는 코드가 추가됐습니다.

또한 인증/인가 프로세스의 추가적인 개선 가능성을 모색했습니다. 정보를 추가적으로 저장하는 것에 비해 얻는 보안상에 이점을 더 갖고싶었기 때문입니다.

그 과정에서 기존 로그아웃 프로세스를 점검하던 중 로그아웃 이후에도 기존 액세스 토큰이 유효해 재사용이 가능하다는 문제또한 발견할 수 있었습니다.

JWT 안전성 개선 - UUID를 활용한 추가 보안 로직

기존의 GatewayFilter입니다.

토큰의 유효성과 사용자의 권한 및 상태를 확인하는 절차를 가집니다.

로그아웃시 사용자의 쿠키에 있는 정보는 지워지지만 그 정보를 기억하고 있다면 토큰 자체는 유효하기 때문에 Filter를 통과해 요청을 보낼 수 있게됩니다.

기존에는 이를 처리할 방법이 없었지만 추가된 UUID를 통해서 이제는 해결이 가능합니다.

  1. 사용자가 로그아웃하면, Redis에서 해당 UUID에 매핑된 정보를 즉시 삭제
  2. Gateway에서 Redis 상태를 조회하도록 로직 추가
  3. 이로 인해 이후의 액세스 토큰 검증 과정에서 UUID를 찾을 수 없게 되어 토큰을 Invalid로 판단
  4. Redis와 UUID를 활용한 상태 검증으로 로그아웃 이후 액세스 토큰 재사용을 완벽히 차단

인증서버에 로그아웃시 UUID정보를 삭제하는 로직 추가 후 

Gateway에도 UUID를 기반으로 유저를 찾는 로직을 추가해줬습니다.

그 후 Filter의 리펙토링 과정을 거치게되면... 

redisService의 isValidateUser를 이용해 액세스 토큰 재사용을 막은 후 토큰과 유저의 권한 및 상태를 확인하게 됩니다.

이 Filter를 기반으로 최종적으로는 다음과 같은 프로세스가 완성됩니다.



이렇게 단계별로 JWT의 안전성을 개선한 여정은 마무리됩니다.
같이 보시면 좋은 발표 영상입니다.

GPT 요약

My-Books 프로젝트에서 Spring Security를 검토했지만, MSA 환경에서의 인증정보 불일치와 자원 소모 문제로 사용하지 않았습니다.
대신, 간단한 인증/인가 요구사항을 충족하기 위해 Spring AOP를 활용해 효율적인 인가 처리를 설계했습니다.
이를 통해 사용자 경험을 개선하고 시스템 자원 소비를 최소화했습니다.

My-Books 프로젝트에서의 경험을 다룬 글입니다.

My-Books 프로젝트에서 저의 주된 역할은 인증/인가 프로세스를 설계하고 구현하는 것이었습니다.
처음에는 당연히 Spring Security를 기반으로 구현하려 했지만 결과적으로는 선택하지 않았는데요 그 과정을 얘기하려 합니다.

Spring Security란?

Spring Security는 Spring Framework 기반의 애플리케이션에서 인증(Authentication)과 인가(Authorization)를 손쉽게 구현할 수 있도록 제공되는 강력한 보안 프레임워크입니다.

사용자가 애플리케이션에 로그인하면, Authentication 객체가 생성되며 Authentication 객체는 Spring Security의 SecurityContext에 저장되어 애플리케이션 전반에서 사용자의 인증 상태를 관리합니다.

또한 Filter Chain을 통해 모든 HTTP 요청을 가로채고, 보안 검사를 수행합니다.

사용하지 않은 이유

Spring Framework에 완벽하게 호환되면서 쉽게 구현이 가능하다니.. 사용하지 않을 이유가 없어 보입니다.
하지만 다음과 같은 이유로 사용하지 않았습니다.

기존의 Spring Security를 활용한 접근 방식에서는 다음과 같은 문제가 있었습니다.

Security Context의 오버헤드

Security Context는 기본적으로 세션에 저장하여 상태를 유지하기 때문에 MSA로 구성된 My-Books 프로젝트에서는 인증정보 불일치 문제가 발생 가능합니다.

이 문제는 SessionCreationPolicyStateless하게 설정하면 해결이 가능하지만
세션을 사용하지 않을 뿐 매 요청마다 SecurityContext를 생성하기 때문에 추가적인 자원 소비가 이뤄지게 됩니다.

이는 서버에 불필요한 오버헤드를 발생시켰고, MSA 환경에서 효율적이지 않다고 판단했습니다.

과정의 복잡성

Spring Security는 내부적으로 DelegatingFilterProxy가 요청을 먼저 받고, 이후 여러 Security 필터를 거치는 구조로 동작합니다. 이 과정이 간단한 인가 로직을 구현하는 데 불필요하게 복잡하다고 생각했습니다.

테스트 코드의 생산성 저하

Security가 활성화된 상태에서는 테스트 코드 작성 시 Security 옵션을 비활성화하거나, Security가 제공하는 User 객체를 주입해야 했습니다. 이는 유닛 테스트 시 불필요한 설정을 요구해 생산성을 떨어뜨렸습니다.

해결 방법

위 문제를 해결하기 위해 다음과 같이 인증/인가 프로세스를 다음과 같이 설계했습니다.
1. JWT 기반 인증:
• 로그인 시, 액세스 토큰을 쿠키에 저장하고 리프레시 토큰을 Redis에 저장.
• 모든 요청 시 클라이언트는 액세스 토큰을 헤더에 담아 전송.
2. Spring Gateway에서 인가 처리
• Gateway의 AbstractGatewayFilterFactory를 상속받아 커스텀 필터를 구현
• 이 필터는 헤더에서 JWT를 추출하고, 유효성을 검증한 후 권한을 확인

하지만 이 과정에서 예외사항에 대한 처리의 필요성이 생겼고(Invalid 토큰,유효기간 만료,권한 불일치..) 이를 해결하기 위해 횡단 관심사를 처리할 수단을 찾게됐습니다.

Filter vs Interceptor vs AOP

Spring Security의 대안으로 생각한 것은 Filter, Interceptor, AOP였습니다.
위에서 언급한 것들은 모두 횡단 관심사를 처리할 수 있는 수단으로, 인가 처리에 사용 가능합니다.

이들은 비슷해 보이지만 차이점이 존재했는데요. Filter는 Spring 스펙이 아닌 Servlet의 스펙으로, DispatcherServlet 이전에 실행되어 모든 HTTP 요청을 대상으로 작동합니다. 그렇기 때문에 Filter는 Spring 컨테이너와 무관하게 작동하므로, Spring Bean이나 컨트롤러 또는 서비스 계층의 로직과 연동된 처리는 어렵습니다.

Interceptor는 Spring MVC에서 제공하는 기능으로, 컨트롤러 실행 전후에 추가 로직을 삽입하는 데 사용됩니다.
Spring MVC 컨트롤러 수준에서 동작하기 때문에 Filter보다 Spring Application과 밀접하게 협력한다는 장점은 있지만, 컨트롤러보다 추상화 정도가 낮은(실제 비즈니스 로직이 존재하는) 서비스 계층에서의 상세한 처리는 불가능합니다.

또한, Filter와 Interceptor 모두 요청 전과 후에 대한 처리를 지원하지만, 이를 하나의 메서드에서 전후를 동시에 처리하는 방법은 없기 때문에 AOP를 선택하게 됐습니다.

내가 사용한 Spring AOP

현재 My-Books는 Front Server에서 사용자 요청 시 Gateway Server로 전달되어 인가 처리를 하는 구조를 가집니다.

또한 인가 처리 실패 시 에러가 발생하고 Front Server는 그에 따른 알맞은 응답을 반환해 사용자의 사용성을 높이는 목표가 존재했습니다.따라서 호출 후 에러발생에 따른 추가적인 로직 실행이 가능해야 됐으므로 Spring AOP의 @Around를 사용하기로 결정했습니다.

인가 처리가 실패하는 경우에 따라서 액세스 토큰을 재발급하거나 특정 페이지로 보내는 등의 작업을 상세 설계하여
사용자의 사용성을 높일 수 있었습니다.

이와 관련해 추가적인 링크 남기면서 마무리 하겠습니다.

------------------------------------

2025/09/02 추가 


이 글이 조회수가 많이 나오는 글이라, 추가적으로 작성합니다. 
위처럼 코드짜면 큰일납니다. 

문자열 매칭 기반 예외 처리, ContextHolder 사용, 난잡한 책임 등의 문제가 있습니다. 
문자열 매칭 보다는 에러코드등으로 대신할 수 있으며, 에러또한 ResponseErrorHandler 를 통해 커스텀 예외로 바로 잡을 수 있습니다. 

더불어 ContextHolder은 ThreadLocal 기반으로, 비동기처리나 Vitrtual Thread 사용시 문제가 발생할 수 있습니다. 

만약 다시 구현한다면
이미 중간에 Gateway를 두고있고 거기서 인가처리를 하고있기 때문에 이런 방법을 생각할 것 같습니다.

1. Gateway에서 인가처리 -> 실패 -> Gateway 에서 리프래시 확인 -> 리프래시 없으면(실패)/있으면 재발급 후 요청 마저 처리
2. 지금처럼 Gateway에서 에러 던짐 -> ResponseErrorHandler 받음 -> JWT 관련 처리하고 재시도

다른 방법도 여러가지 많겠지만 일단, AOP의 책임을 줄이는 방식으로 구현하는것은 확실하다 생각합니다. 
지금은 너무 책임이 장황하고 많이 가지고 있는 것 같아요 

또한 JWT를 사용하면서 Redis에 저장해 사용하고있는데, 이는 보안성을 높이기위한 방법입니다. 
이러면 세션과 똑같이 구현되는거 아니야? 왜 JWT써? 라고 한다면 Qeury Ratio가 달라집니다. 

세션은 항상 Session DB (지금 경우에는 Redis)를 찔러야하지만 JWT의 경우 클라이언트에게 토큰정보가 존재하기 때문에
AccessToken이 만료된 경우에만 DB를 찌르게 됩니다. 

DB에 쿼리날리는 빈도 자체가 달라지기 때문에 충분히 의미가 있다고 생각하며 
RefreshToken(RT) 자체도 JWT 토큰의 형식이 아닌 커스텀해서 가져갈 수 있습니다. 

RT의 JWT형식을 유지하는 이유는 보편적으로는 클라이언트에게 RT가 있기 때문에 이를 검증하기 위해 쓰는 것 인데 
지금같은 상황에서는 RT를 서버에서 관리하고 있어, Redis TTL 로 만료 처리를 하고 RT는 간단한 문자열 혹은 정수값이어도 상관 없습니다.

Redis TTL의 경우 Policy가  Active/Lazy 하게 만료되는 정책이 있으며 
기본적으로 두 정책을 섞어 사용하는 것으로 압니다. 

Active = 조회시 TTL 체크 넘었으면 삭제 (조회안된 것들은 계속 남아있어 메모리 먹는 문제 발생)
Lazy = 조회안된 것들을 백그라운드에서 스케줄러 돌려 삭제처리 

그 외에 메모리가 꽉 찼을때 어떤 데이터를 삭제할것이냐 라는 삭제 정책도 있지만, 이는 TTL과는 관련없기 때문에 
필요하다면 찾아보시길 바랍니다. 

또한 여전히 Spring Security를 사용해 강결합되는 것은 고민이 필요한 문제입니다. 
몇가지 Interface나, 유용한 어노테이션을 제공해주긴 하지만.. 대부분 커스텀해서 == 구현해서 사용해야하고

동작하는 방식도 복잡하며 무거운 프레임워크인 만큼 AutoConfig 되는 빈들이 많은데 충분한 이해가 없는 상태에서 사용하기에는 
이점이 없다 생각합니다. 

추가적으로 데스트코드 작성할때도 불편해져요 

동작 방식 복잡함 (Filter는 원래 Servlet Spec이라 Container를 왕복해줘야 합니다.)

(ServletContainer)FilterChain -> DelegateFilterProxy -> (여기서부터 SpringContainer) Security Filter Chain 
-> (Filter 순회 다 끝남 이제 SevletContainer 로 돌아가서 요청 진행) DispathcerServlet -> Handler Mapping -> AdpatorMapping -> (SpringContainer)Controller 호출 및 로직처리

따라서.. 간단하게 RBAC 정도만 하면 된다. 
관리자/유저 정도고 디테일한 인증/인가 처리가 필요하진 않다.
나는 Spring Security에 대한 완벽한 이해가 없다 

그렇다면 커스텀하게 인증/인가 처리하는게 더 좋다고 생각합니다. 
오히려 현업에서는 Security와 강결합된 코드를 레거시로 보는 경우도 많은 것 같아요 
 
지금 다시 보니 너무나 부족한 글인데.. 읽어주셔서 감사합니다.
 

 

+ Recent posts