JPA 상태 패턴 적용하기
·
spring
상황 결재 시스템에 필요한 상태를 아주 간략하게 표현했다.사용자가 결재를 저장하고 상신요청을 하게되면 결재 라인을 타게되어 결재자가 기안문서를 볼 수 있다.이후 결재 라인에 결재자 A, B, C 가 존재한다고 가정하고 정상적인 시나리오만 작성해 본다.모두 승인하는 케이스사용자가 기안을 올리면 상태는 저장에서 상신요청으로 변경된다.A가 결재를 승인하면 결재 상태는 상신 요청에서 진행중으로 변경된다.B가 결재를 승인하면 결재 상태는 그대로 진행중이다.C가 결재를 승인하면 결재 상태는 진행중에서 완료로 종결된다.한명이라도 반려하는 케이스A가 결재를 승인하면 결재 상태는 상신 요청에서 진행중으로 변경된다.B가 결재를 반려하면 결재 상태는 반려로 종결된다.문제점위 상태 흐름대로 해피 케이스만 실행되면 좋지만 항상..
Spring Security DSL 권한 계층 설정하기
·
spring
권한 계층이 필요한 이유쉽게 생각해 볼 수 있는 권한 체계는 계층이 있는 권한이다.간단하게 게시판 서비스에 권한이 3개 존재한다고 가정해 보자.일반 사용자: 게시글 조회만 가능VIP: 글 작성 가능관리자: 삭제/수정 관리 가능이때 VIP 권한에 일반 사용자 권한이 가지고 있는 게시글 조회 권한이 없다는 것은 상식상 이해할 수 없다.마찬가지 관리자가 게시글 조회 및 작성 권한을 가지는 것이 당연해 보인다. 스프링 시큐리티에서는 기본적으로 권한마다 허용 가능한 API를 별도로 설정해야 하는데,이러면 공통 허용 API가 추가될 때 마다 공통 권한을 만들고 공통 권한을 부여해야한다. 이런 문제를 해결하고자 스프링 시큐리티에서는 RoleHierarchy를 제공한다.스프링부트 3.2.3 기준으로 작성되어 있습니다...
헥사고날 기반 아키텍처
·
spring
회사 프로젝트의 기존 레이어드 아키텍처에서 DIP 원칙을 지켜 설계했지만,애매한 부분들이 있었고 이런 문제로 헥사고날 기반의 아키텍처를 적용해 봤습니다.헥사고날 아키텍처(포트와 어댑터)란?구글, 유튜브에 검색하면 질 좋은 글이나 영상이 많이 있어 이 글에서는 생략하도록 하겠습니다.구성 레이어헥사고날 아키텍처에서 레이어는 다음과 같이 구분됩니다.Inbound adapterUse caseDomainOutbound adapter각 레이어가 어떤 역할을 하는지 하나씩 알아보겠습니다.Inbound adapter(ui)사용자의 요청을 처리하는 영역입니다.외부에서 요청해야 동작한다고 하여 Driving Side Adapter 또는 Primary Adapter 로 불립니다.e.g.HTTP API (RestControl..
OAuth 2.0 + PKCE
·
보안
OAuth 2.0 흐름을 알기 위해서 몇 가지 알아야 할 키워드가 있다. OAuth 2.0 Rolse Resource Owner (자원 소유자) 보호된 자원에 대한 접근 권한을 부여할 수 있는 주체, 사용자로서 계정의 일부에 대한 접근 권한을 부여하는 사람 사용자를 대신하여 작동하려는 모든 클라이언트는 먼저 사용자의 허가를 받아야 한다. 즉, 서비스를 사용하려는 사용자이다. Resource Server (자원 서버) 타사 애플리케이션에서 접근하는 사용자의 자원이 포함된 서버를 의미 액세스 토큰을 수락 및 검증할 수 있어야 하며 권한 체계에 따라 요청을 승인할 수 있어야 한다. 카카오, 네이버, 구글, Github를 예로 들 수 있다. Authorization Server (인가 서버) 클라이언트가 사용자..
자바스크립트의 이벤트 루프
·
javascript
이벤트 루프에 대해 검색해 보면 싱글 스레드에 대해 얘기한다. 싱글 스레드는 멀티 스레드가 아니므로 하나의 작업만 수행할 수 있다. 다시 말해 어떤 작업 목록이 있다면 순차적으로 처리하는 방식이 싱글 스레드이다. 브라우저에서 보이는 작업들이 싱글 스레드이지만 동시에 처리되는 것 같은 이유는 컴퓨터의 연산 속도가 매우 빠르므로 동시에 돌아가는 것처럼 착각하고 있는 것이다. 궁금증 앞에서 설명한 내용은 간단해서 이해가 빨랐다. 그런데 다음과 같은 고민은 쉽게 풀리지 않았다. 싱글 스레드인데 네트워크 작업이 처리되는 동안 다른 작업들은 어떻게 할까? 마찬가지 setInterval() 같은 함수를 사용했을 때 시간을 재고 있는 동안 다른 작업을 어떻게 처리하지..? 지금 보면 쉽게 생각하겠지만 한창 비동기에 대..
AOT(Ahead-Of-Time) 컴파일을 하면 얼마나 빠를까?
·
spring
최근 스프링 발표 세션을 듣거나 블로그들을 돌아다니다 보면 GraalVM에 대해서 언급한다. 공통 내용 중 한 가지는 GraalVM의 기능 중 AOT 컴파일러를 이용하면 Native 이미지 또는 실행 파일을 생성할 수 있게 도와준다고 한다. 즉, JIT을 사용하여 바이트코드를 매번 기계어 코드로 변환하는 것이 아닌 해당 머신의 기계어 코드를 바로 실행시킬 수 있게 하는 것이다. 이번 포스팅에서 자세한 내용을 다루기보다는 얼마나 빠른지 체감을 할 수 있는 정도이다. (자세한 내용은 다른 블로그에서 잘 설명하고 있다.) Native 빌드 방법 Gradle GraalVM 플러그인을 사용하면 Native Image를 만드는 방법 말고도 Native 실행 파일을 직접 만들 수 있게 제공한다. 스프링 공식 문서를 ..
JPA 환경에서 TSID(Time-Sorted Unique Identifier) 생성하기
·
spring
UUID 단점 중 하나 UUID는 무작위로 값을 생성하기 때문에 순서가 보장되지 않는다. 이런 이유로 데이터베이스에서 UUID 값을 인덱스로 사용한다면 인덱싱이 어려울 수 있다. TSID로 해결 UUID 인덱싱의 어려움을 해결할 수 있는 방법은 정렬을 하는 것이다. 시간 기반으로 정렬되고 중복되지 않는 고유한 식별자인 TSID를 이용하면 어느정도 해결할 수 있어 보인다. 사용 방법 하이버네이트의 UuidGenerator 어노테이션을 사용하면 손쉽게 TSID를 생성할 수 있다. (하이버네이트 6.0 버전 이상 사용 가능) TIME Enum 값을 보면 시간과 IP 기반으로 생성한다는 것을 확인할 수 있다. 이는 다수의 인스턴스 환경에서 같은 시간에 접근해도 고유한 값을 가질 수 있다는 것을 의미하는 것 같다..
스프링부트 ErrorController 동작 원리
·
spring
스프링부트 서버에 알 수 없는 요청을 보내거나 서버에서 에러가 발생하면 기본 설정인 BasicErrorController.class에 의해 에러 페이지 또는 에러 메시지를 응답한다. 💡 Controller 계층에서 발생하는 특정 예외를 처리해야 하는 경우 ExceptionHandler 또는 ControllerAdvice를 활용합니다. Error 응답 동작 원리 컨트롤러 계층에서 처리할 수 없는 예외가 던져지면 콜스택을 따라 컨트롤러를 호출한 서블릿으로 전파된다. 서블릿 컨테이너(톰캣)는 전파된 예외를 받으면 에러를 응답하게되고 이후 스프링부트는 다시 서블릿으로 부터 에러를 받으면 /error로 전달한다. /error라는 경로는 ErrorProperties 기본값으로 정의되어 있다. 디버깅을 해봤는데 톰캣..
서버는 최초 클라이언트의 IP를 어떻게 알 수 있을까?
·
네트워크
설명을 위해 아이피를 4자리 숫자로 표기했습니다. 클라이언트가 서버에 HTTP 요청을 한다고 가정해 보자. 클라이언트와 서버 사이의 네트워크 Hop은 1개이며, 서버는 클라이언트의 패킷을 받아 출발지의 IP(1000)를 보고 판단할 수 있다. 여기까지는 이해하기 쉽다. 이제, 클라이언트와 서버 사이에 프록시 서버를 둔다고 가정해보자 클라이언트가 서버에 접속하기 위해서는 다음과 같은 절차를 거친다. 클라이언트는 서버에 접근하기 위해 프록시에게 HTTP 요청을 한다. 프록시는 요청을 받고 서버에게 전달한다. 서버는 프록시로부터 받은 패킷을 열어본다. 이렇게 된다면 서버는 프록시로부터 받은 패킷을 봤을 때 프록시의 IP(2000)가 찍히게 된다. 이런 상황에서 어떻게 서버는 클라이언트의 주소를 알 수 있을까?..
Spring REST Docs 필수값 표현하기 + Tip
·
spring/테스트
아무런 설정없이 문서를 작성하면 요청 파라미터나 요청 헤더 값에 대한 설명은 작성할 수 있지만 필수값(Required) 표현이 안 되었다. 그 이유는 기본적으로 제공되는 스니펫 규격이 정해져있기 때문이다. default-request-headers.snippet 파일 |=== |Name|Description {{#headers}} |{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}} |{{#tableCellContent}}{{description}}{{/tableCellContent}} {{/headers}} |=== 스니펫들 중 하나를 선택해 읽어보면 name, descripton 속성만 있는 것을 확인할 수 있다. 필수값을 표현할 수 있는 방법을 알아보던..
noose
noose