본문 바로가기

분류 전체보기

(96)
조금 많이 늦은 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 ..
Deque(C++) Deque Deque는 데이터를 담는 자료구조로, Stack, Queue와 다르게 데이터를 양방향으로 저장하는 자료구조이다. Deque는 데이터를 가장 앞(front)에 저장할 수 있고, 데이터를 가장 뒤(back)에 저장할 수 있다. Stack과 Queue를 합쳐 놓았다고 생각하면 쉽다. Queue는 다음과 같은 연산을 가진다. 1. pushFront() - 맨 앞(Front)에 데이터를 넣는다. 2. pushBack() - 맨 뒤(back)에 데이터를 넣는다. 3. popFront() - 맨 앞(Front)에서 데이터를 pop 한다. 4. popBack() - 맨 뒤(Back)에서 데이터를 pop 한다. 5. peekFront() - 맨 앞(Front)의 데이터를 확인한다. 6. peekBack() ..
2230번: 수 고르기(Java) 투 포인터를 사용해 해결할 수 있다. 우선, 필요한 변수들을 선언한다. int N, M; Scanner scanner = new Scanner(System.in); N = scanner.nextInt(); M = scanner.nextInt(); int[] arr = new int[N]; int p1 = 0; int p2 = 0; int result = 2000000000; |A[i]|의 범위가 0~1000000000 이므로, 두 수의 차이는 최대 2000000000까지 될 수 있다. 따라서 result를 우선 2000000000로 설정했다. arr에 데이터를 넣어 주고, 투 포인터를 사용해야 하기 때문에 arr를 정렬해준다. Arrays.setAll(arr, i -> scanner.nextInt());..