본문 바로가기

분류 전체보기

(98)
Spring Boot에서 Redis를 활용해 데이터 캐싱하기(feat. Look Aside) 캐시는 자주 사용되는 데이터를 미리 복사해 저장하는 임시 저장소이다. 다음과 같은 상황에서 캐시를 사용할 수 있다. 같은 데이터에 반복적으로 접근하는 상황동시에, 해당 데이터의 변경이 적은 상황캐시를 구현하기 위해 Redis가 많이 사용된다. 그 이유는 다음과 같다.Redis는 In memory 데이터베이스로, Key, Value 쌍을 저장하는 일종의 NoSQL DB이다. In memory 데이터베이스이므로, 하드 디스트에 데이터를 저장하는 일반적인 데이터베이스보다 속도가 월등히 빠르다. 물론, 단점도 있다. 접근 속도에 대한 Trade Off로 비싸다. 비싸다는 것은, 용량이 적다는 것을 나타낸다. 따라서, 많은 데이터를 Redis에 저장하기엔 한계가 있다. 캐싱을 잘 사용하면 특정 상황에서 애플리케이..
캐싱, 개념과 읽기 전략들 다음과 같은 상황을 가정해보자. 상품 판매 서비스를 개발하고 있다.상품 조회 API를 개발하고 있는데, 상품의 정보는 거의 바뀌지 않지만, 매 요청마다 DBMS에 쿼리를 날려 다시 조회하고 있다. 이 상황의 문제점은 다음과 같다. 변경 빈도가 매우 낮은 데이터를 얻기 위해 매번 DB에 쿼리를 보내 애플리케이션의 성능이 떨어지고 있다! 이것이 왜 문제일까? 1. DBMS는 기본적으로 Disk에 데이터를 저장한다. Disk는 용량은 크지만, 접근 시간이 느리다는 단점이 있다.2. 상품 데이터가 100만개라고 가정하면, 매 조회마다 Disk에 접근해야 한다.3. 매번 DBMS에 쿼리를 날리지만, 응답 데이터는 거의 항상 똑같다.  이 비효율성을 해결하기 위해 똑똑한 사람들이 한 가지 방법을 생각해냈다. 변경이..
조금 많이 늦은 11기 마무리와 12기 운영진 시작 11기에서 활동하며 "개발"이라는 것을 처음 접했다. 쉬운 알고리즘 문제나, 심심풀이로 구현을 해 보는 게 전부였던 나에게 11기 활동은 정말 큰 행운이었다. 웹 개발을 처음 접하고, 백엔드 개발도 처음 접했다. 아무것도 모르는 상태에서 시작했기에 세션을 따라가기 힘들었지만, 개발이 즐거웠기에 버틸 수 있었다. 해커톤을 하며 나보다 뛰어난 팀원과의 소통도 경험해 볼 수 있었고, 스스로에 대한 부족함도 많이 느꼈다.해커톤 당시에는 자괴감이 많이 들었다. 지금도 마찬가지지만, 세상은 매우 넓고 나보다 뛰어난 사람은 널렸다...이 자괴감은 곧 지식에 대한 갈증으로 변화되었다. 해커톤이 끝나고, 진정한 방학이 시작되었지만, 해커톤 코드를 리팩터링하고 부가적인 기능을 계속해서 추가하며 스스로 공부하기 시작했다...
Interceptor를 활용한 JWT 토큰 검증 인증, 인가를 위해 매 요청마다 JWT를 검사해야 한다.하지만, 이 과정을 비즈니스 로직 내부로 가져오기엔 무리가 있다. 인증, 인가는 비즈니스 로직의 책임이 아니다. 비즈니스 로직을 구현하는 객체는 자신의 책임에만 집중해야 한다.유지보수성이 좋지 않다. Util Class로 분리해도, 결국 Service들은 해당 Util Class에 의존해야 하기 때문이다. 즉, Class 간 결합도가 높아진다. 인증, 인가를 비즈니스 로직에서 빼내보자.Controller에 요청이 도착하기 전에 인증, 인가가 수행할 것이다.Controller 앞에서 구현하면 다음과 같은 이점을 가질 수 있다. 모든 요청이 Controller에 도달하기 전에 인증과 인가가 처리되므로, 인증되지 않은 접근이나 권한이 없는 요청을 사전에 ..
Dto Validation 실패 시 예외처리 우리는 Dto를 사용하면서 여러 Validation 어노테이션을 사용한다. 학생을 저장하는 요청의 Dto를 살펴보자 @Getter public class ApplicationSaveDto { @NotNull(message = "학번이 누락되었습니다.") @Pattern(regexp = APPLICATION_STUDENT_ID_PATTERN, message = "학번이 형식에 맞지 않습니다.") private String studentId;//학번 @NotNull(message = "이름이 누락되었습니다.") @Pattern(regexp = APPLICATION_NAME_PATTERN, message = "이름이 형식에 맞지 않습니다.") private String name;//이름 @NotNull(mes..
Spring Boot에선 예외를 어떻게 처리할까?(전역, 커스텀 예외처리) 프로그램을 개발하다 보면, 예외를 던지는 상황이 생긴다. try, catch, finally를 사용해 이 예외를 다룰 수 있다. 하지만, 이는 프로젝트 규모가 커지면 코드 중복, 리소스 낭비 등으로 이어진다. Spring Boot에서는 이 예외를 어떻게 다룰까? 우선, Spring Boot는 실행 중 발생한 예외는 기본적으로 처리해 준다.   하지만, 이런 Response는 프론트엔드 입장에서 알아보기 어렵다. 회원가입 시 이름, 이메일, 전화번호를 POST로 우리 서버에 보내는 상황에서 HTTP 400 만으로는 이름이 잘못되었는지, 전화번호가 잘못되었는지 알 수 없다. (Message를 사용하긴 하지만, 프론트의 코드로 Message를 구별하긴 힘들다. 특히 규모가 커질수록…) 이런 문제가 생기지 않..
3일 만에 해킹 시도 630회? 해외 IP 공격을 막은 AWS WAF 도입기 회원 모집 프로젝트 배포 후, 예상치 못한 대규모 공격 시도에 직면했다.개발자라면 한 번쯤 겪을 수 있는 실전 보안 경험을 공유하고, 이를 AWS WAF로 어떻게 해결했는지 간단하게 정리해 보았다. 1. 문제 상황배포 후 서버 로그를 분석하던 중, 매일 평균 약 210회의 무분별한 해킹 시도 정황이 포착되었다. 공격 유형은 주로 시스템 환경 변수나 인증 정보를 획득하기 위한 GET API 요청이었다. 이는 서비스의 취약점을 탐색하려는 전형적인 공격 패턴이다. 2. 문제 파악을 위한 로깅단순히 공격 시도 횟수만으로는 대응이 불가능했기 때문에, 공격의 출처를 파악하기 위한 로깅 시스템을 구축했다. 수집 정보정보수집 이유Request IP공격자의 위치 파악HTTP Method어떤 방식으로 요청했는지 확인R..
Dto에 Validation 적용하기 build.gradle에 다음을 추가한다. implementation 'org.springframework.boot:spring-boot-starter-validation' validation 어노테이션 목록은 다음과 같다. @Null - // null만 혀용합니다. @NotNull - // null을 허용하지 않습니다. "", " "는 허용합니다. @NotEmpty - // null, ""을 허용하지 않습니다. " "는 허용합니다. @NotBlank - // null, "", " " 모두 허용하지 않습니다. @Email - // 이메일 형식을 검사합니다. 다만 ""의 경우를 통과 시킵니다. @Email 보다 아래 나올 @Patten을 통한 정규식 검사를 더 많이 사용합니다. @Pattern(regexp..
Spring Security jwt 적용, 커스터마이징 Spring Security, jwt를 사용하기 위해서 build.gradle의 dependencies에 다음을 추가한다. implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'io.jsonwebtoken:jjwt-api:0.11.5' implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' 그 다음, SecurityConfig 를 작성한다. SecurityConfig는 Spring Security의 보안 설정을 정의하는 역할을 한다. 보안 구성 및 규칙, 세션 및 로그인 , C..
Spring Security 구조와 동작 원리 Spring Security Spring Security란, Http Request가 DispatcherServlet으로 도달하기 전 Servlet의 Filter를 기반으로 Request에 대한 인증과 인가를 적용시켜주는 프레임워크이다. 일반적으로 클라이언트에서 서버로 요청을 보내면, DispatcherServlet이 하나의 HttpServeletRequest를 받아서 요청을 처리하고 HttpServletResponse 응답을 클라이언트로 보낸다. 그런데, 하나 이상의 Filter가 포함된다면, 클라이언트에서 보낸 요청이 Servlet으로 전달되기 전에 Filter를 거치게 된다. 클라이언트가 애플리케이션에 하나의 요청을 보내면, 컨테이너는 하나의 FilterChain을 생성한다. FilterChain ..