Spring Boot Web Starter에서 validation 애노테이션 없어짐

배경 Spring Boot에서 @NotBlank를 사용했는데 존재하지 않는 애노테이션으로 인식했다. Spring Boot의 웹 스타터에 validation api를 구현한 하이버네이트 컴포넌트가 존재한다고 알고 있었는데, 의문이었다. 원인 및 해결 Spring Boot 2.3 부터 웹 스타터에서 validation 스타터가 분리되었다. 아래와 같이 직접 의존을 추가해줘야 된다. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> dependencies { ... implementation 'org.springframework.boot:spring-boot-starter-validation' } 참고 자료 https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#validation-starter-no-longer-included-in-web-starters https://stackoverflow.com/questions/48614773/spring-boot-validation-annotations-valid-and-notblank-not-working

2024-09-15 · 1 min · 54 words

Spring Batch 시작하기

목표 Spring Batch의 구조를 이해한다. Spring Batch 사용법을 이해한다. Spring Batch 구조 스프링 배치는 Application, Batch Core, Batch Infrastructure 3가지 컴포넌트를 가진다. Application: 개발자가 작성하는 모든 배치 job과 사용자 정의 코드가 있다. Batch Core: 배치 작업을 시작하고 제어하는 데 필요한 핵심 런타임 클래스가 포함되어 있다. JobLauncher, Job, Step 구현체가 존재한다. Batch Infratstructure: 애플리케이션 개발자와 배치 코어에서 사용하는 클래스들이 포함되어 있다. ItemReader, ItemWriter RetryTemplate 하나의 Job에는 여러 개의 Step이 있고, Step에는 ItemReader, ItemProcessor, ItemWriter를 하나씩 가지고 있다....

2024-09-15 · 3 min · 452 words

Spring AOP 시작하기

목표 AOP가 무엇인지 이해한다. Spring AOP를 사용하는 방법을 이해한다. Spring AOP의 내부 구조를 이해한다. AOP(Aspect-Oriented Programming) Cross-Cutting Concern concern: 기능에 따라 분리한 시스템의 동작 concern의 2가지 종류 core concern: 주요 요구사항에 대한 기능 cross-cutting concern: 보조되는 또는 시스템 전반적인 요구 사항 예시: 로깅, 보안, 데이터 전송 등 aspect: cross-cutting concern을 모듈화한 것 AOP cross-cutting concern을 분리하여 모듈성을 높이는 것을 목표로하는 프로그래밍 패러다임 새로운 동작이 필요하면, 기존 코드에 추가하지 않고 분리된 새로운 코드를 선언할 수 있다....

2024-09-15 · 3 min · 503 words

sort 기준 변경

sort() 함수의 key 파라미터를 통해서 정렬 기준을 변경할 수 있다. 이 파라미터는 람다를 파라미터로 받는다. 람다는 요소를 파라미터로 받고, 우선 순위가 높은 정렬 기준을 앞으로 오도록 하는 튜플을 반환하면 된다. 역순으로 정렬하고 싶다면 -x[0] 와 같이 두면 역순으로 정렬된다. temp.sort(key=lambda x : (x[0], x[1])) # '-'부호를 이용해서 역순으로 가능 좀 더 복잡한 비교 연산이 필요한 경우 함수를 사용해야된다. functools 모듈을 불러와서 functools.cmp_to_key() 의 파라미터로 자신이 커스텀한 함수를 주면 된다. 왼쪽의 값이 먼저 오고싶다면 -1을, 오른쪽 값이 먼저 오고싶다면 1을, 같다면 0을 반환하면된다....

2024-09-15 · 1 min · 122 words

socket io란

socket.io란? WebSocket 등으로 양방향 통신을 가능하게 해주는 라이브러리다. 2가지 주요 기능으로 분류할 수 있다. Node.js 서버를 위한 라이브러리 클라이언트의 JavaScript 라이브러리 socket.io는 JavaScript뿐만아니라 Java, C++, Swift 등의 다양한 언어를 지원해준다. socket.io의 주요 기능 HTTP long-polling fallback: 브라우저가 WebSocket 연결에 실패할 경우 long polling 방식으로 통신을 하도록 만들어준다. Automatic reconnection: heartbeat 매커니즘으로 연결 상태를 확인하고, 연결이 끊겼을 때 자동으로 재연결한다. Broadcasting: 서버에서 연결되어 있는 모든 클라이언트에게 이벤드를 보낼 수 있다. 기본 API 3000번 포트를 사용하는 Node....

2024-09-15 · 1 min · 189 words

Sociable Unit Test vs Integration Test

테스트하고자 하는 객체가 다른 객체에 의존하고 있으면 그것은 단위 테스트가 아닌가? Sociable Unit Test와 Integeration Test는 궁극적으로 테스트의 목표가 다르다. Integration Test는 2개이상의 모듈이 있을 때, 각 모듈의 정확성보다는 제대로 협력하는지를 테스트한다. 따라서 Integration Test는 모든 경우를 모두 테스트할 필요는 없다. Sociable Unit Test는 모든 경우의 수에 제대로 동작하는지 테스트를 한다. 종속되는 대상을 이미 테스트하고 있다면, 굳이 독립적인 테스트를 만들 필요가 없다. 이미 종속되는 테스트를 완료했으며, 만약 너가 TDD를 하고있다면 테스트를 실패하는 원인을 찾기 어렵다는 점도 없을 것이다....

2024-09-15 · 1 min · 81 words

snake case 필드를 camel case로 역직렬화 하기

목표 아래와 같이 snake case로 필드가 존재하는 json을 camel case로 역직렬화 하는 방법이 필요했다. { "first_name": "Jackie", "last_name": "Chan" } 방법1: @JsonProperty 사용 @JsonProperty 어노테이션을 사용하면, 해당 필드와 매핑되는 json 프로퍼티의 이름을 지정할 수 있다. public class UserWithPropertyNames { @JsonProperty("first_name") private String firstName; @JsonProperty("last_name") private String lastName; // standard getters and setters } 방법2: @JsonNaming 사용 @JsonNaming을 클래스 위에 명시하면 현재 클래스의 역직렬화 이름 전략을 설정할 수 있다. 아래와 같이 명시하면 클래스의 필드를 json의 snake case로 찾아서 매핑한다....

2024-09-15 · 1 min · 98 words

setter 메서드로 변경 추적이 되지 않는 문제 해결

Room room = Room.builder() .id(1L) .game(game) .tags(tags) .build(); room.join(user); roomRepository.save(room); 위와 같은 코드가 있는데 roomRepository.getById(1L) 해도 user가 비어있다. @Transactional어노테이션을 붙이니 해결이 됐는데 원인을 모르겠다. 현재 추정: 트랜잭션과 변경 감지 roomRepository.save()를 호출하면 트랜잭션을 기준으로 저장되는 것으로 추정된다. 따라서 위의 코드는 @Transactional 이 없기 때문에 트랜잭션에 모아둔 커밋이 없기 때문에 단순하게 Room Entity 자체만 저장이 되는것같다. (users는 연관관계의 주인이 아니기때문에 저장되지 않는다.) JpaRepository의 코드를 보기로 했다. JpaRepository의 기본 구현체는 SimpleJpaRepository 로 이 부분에 save 메서드가 구현되어 있었다....

2024-09-15 · 1 min · 175 words

Routing

목표 SvelteKit의 라우팅 방법을 이해한다. SvelteKit의 라우팅 SvelteKit은 파일 시스템 기반으로 라우트를 구성한다. 원하는 URL 경로와 일치하는 디렉토리를 코드에 정의하면 된다. src/routes: root 경로 src/routes/: /about 경로의 라우팅 src/routes/blog/[slug]: slug라는 파라미터를 받으면서, /blogs/[slug] 경로의 라우팅 파라미터를 받으려면 동적 라우팅이 필요한데, prerender=true 에서는 사용할 수 없다. SvelteKit이 아닌 https://github.com/jorgegorka/svelte-router 같은 별도의 라우터를 사용해야된다. 모든 route 디렉토리에는 하나 이상의 route 파일이 필요하다. 이는 + prefix로 구분한다. +page, +error, +layout 등 +page +page.svelte 해당 경로의 페이지를 정의한다....

2024-09-15 · 1 min · 142 words

RestAssured로 Rest docs 사용하기

MockMvc로 사용중인 Spring rest docs를 RestAssured로 바꿔보자. 의존 추가 testImplementation 'org.springframework.restdocs:spring-restdocs-restassured' 테스트 코드 private RequestSpecification spec; @BeforeEach public void setUp(RestDocumentationContextProvider restDocumentation) { this.spec = new RequestSpecBuilder() .addFilter(documentationConfiguration(restDocumentation)) .build(); } body 내용을 예쁘게 표시하고 싶다면 아래와 같이 구성하면된다. @BeforeEach protected void setUp(final RestDocumentationContextProvider restDocumentation) throws Exception { this.specification = new RequestSpecBuilder() .addFilter(documentationConfiguration(restDocumentation).operationPreprocessors() .withResponseDefaults(prettyPrint())) .build() ; rest api 호출 RestAssured.given(this.spec) .accept("application/json") .filter(document("index")) .when().get("/") .then().assertThat().statusCode(is(200)); 필드 설명을 추가하고 싶다면 아래와 같이 필터를 지정하면 된다....

2024-09-15 · 1 min · 86 words

REST DOCS가 빌드 시 자동 생성되도록 gradle build 수정하기

의존 추가 없이 구현이 가능했다. asciidoctor에서 최종 문서가 저장되는 위치를 설정하는 방법이 적혀있어서 참고했다. outputDir를 통해 자신이 원히는 경로를 지정할 수 있다. jar 파일을 만들기 전에 build/resources/main/static/docs 경로에 rest docs 파일을 추가하여 수동으로 문서를 커밋하지 않아도 빌드시에 만들어지도록 구현했다. // build.gradle asciidoctor { attributes 'snippets': snippetsDir inputs.dir snippetsDir outputDir "build/resources/main/static/docs" dependsOn test } bootJar { dependsOn asciidoctor } 참고 자료 https://asciidoctor.github.io/asciidoctor-gradle-plugin/development-3.x/user-guide/

2024-09-15 · 1 min · 61 words

ResourceBundleMessageSource 파헤치기

목표 ResourceBundleMessageSource가 다국어 값을 가져오는 과정을 이해한다. ResourceBundleMessageSource에 기본 Locale을 설정하는 방법을 이해한다. ResourceBundleMessageSource Spring Boot에서 다국어를 처리할 때 기본적으로 사용하는 MessageSource 인터페이스의 구현체 액세스한 ResourceBundle 인스턴스와 각 메시지에 대해 생성한 MessageFormat을 캐싱한다. MessageSource Spring에서 만든 메시지를 매개 변수화 및 i18n화 할 때 사용하는 전략 인터페이스 아래 3가지의 메소드가 존재한다. ResourceBundle Java에서 만든 Locale 별로 리소스(예: 문자열)가 필요한 경우 이를 번들로 묶어서 관리해주는 클래스 이를 상속해서 property file로부터 각 Locale 별로 문자열을 관리하는 PropertyResourBundle도 존재한다....

2024-09-15 · 1 min · 161 words

Request Parameter 기본값 지정하기

@RequestParam 어노테이션을 이용해서 지정할 수 있다. defaultValue라는 속성에 자신이 원하는 기본값을 지정해주면 된다. @GetMapping("/api/foos") @ResponseBody public String getFoos(@RequestParam(defaultValue = "test") String id) { return "ID: " + id; } 참고 자료 https://www.baeldung.com/spring-request-param

2024-09-15 · 1 min · 32 words

request body로 enum 값 받기

목표 사용자가 request body로 string 값을 보냈을 때, enum으로 자동으로 매핑시켜서 컨트롤러 파라미터로 넘어오기를 원한다. Spring 기본 처리 Spring MVC에서는 StringToEnumConverterFactory 클래스가 String을 Enum 객체로 변환하려고 시도한다. 이 때, Enum.valueOf() 메소드를 통해서 변환을 시도한다. 이 때 일치하는 Enum 타입이 없다면, ConversionFailedException이 발생한다. Custom Converter 사용하기 Converter 인터페이스를 이용해서 변환을 커스텀 할 수도 있다. public class StringToEnumConverter<T extends Enum<?>> implements Converter<String, T> { private final Class<T> type; public StringToEnumConverter(Class<T> type) { this....

2024-09-15 · 1 min · 88 words

Repository를 사용하여 데이터 개수 제한하기

top이나 first를 사용하면된다. 따로 개수를 표기하지 않으면 1개의 데이터만 가져온다. User findFirstByOrderByLastnameAsc(); User findTopByOrderByAgeDesc(); Page<User> queryFirst10ByLastname(String lastname, Pageable pageable); Slice<User> findTop3ByLastname(String lastname, Pageable pageable); List<User> findFirst10ByLastname(String lastname, Sort sort); List<User> findTop10ByLastname(String lastname, Pageable pageable); 참고 자료 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.limit-query-result

2024-09-15 · 1 min · 37 words

Redis 서버 구성하기

설치 및 구성 redis-server를 실행한다. $ sudo apt update $ sudo apt install redis-server redis-server가 서비스 환경으로 구동될 수 있도록 설정 supervised systemd $ sudo systemctl restart redis.service 외부에서 접근 가능하도록 수정 bind 0.0.0.0 $ sudo systemctl restart redis 패스워드 설정 requirepass ${내가 원하는 패스워드} $ sudo systemctl restart redis 확인 참고 자료 https://xerar.tistory.com/80 https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-ubuntu-18-04 https://infoscoco.com/92

2024-09-15 · 1 min · 56 words

Redis Transaction

목표 redis를 이용한 작업 중에서 원자적으로 동작해야되는 로직이 필요했다. 이를 위해 redis에서 transaction을 사용하는 방법을 이해한다. 방법 Redis에서 MULTI가 트랜잭션의 시작을 나타내고 EXEC가 트랜잭션의 커밋, DISCARD가 롤백을 나타낸다. RedisTemplate 자체애도 multi(), exec()가 있지만, 각 커맨드가 같은 connection에서 실행된다는 보장이 없다. 이를 위해 SessionCallback 인터페이스로 하나의 커넥션에서 수행할 연산을 전달한다. //execute a transaction List<Object> txResults = redisTemplate.execute(new SessionCallback<List<Object>>() { public List<Object> execute(RedisOperations operations) throws DataAccessException { operations.multi(); operations.opsForSet().add("key", "value1"); // This will contain the results of all operations in the transaction return operations....

2024-09-15 · 1 min · 97 words

Redis Lock 사용하기

목표 Distributed Lock의 목적을 이해한다. Spring Integration을 이용해서 Redis Lock을 사용해본다. Distributed Lock 자바에서 제공하고 있는 Lock은 하나의 프로세스에서 생성되는 여러 스레드에서 공유 자원에 접근할 때, 스레드간 동기화를 위해서 사용한다. 따라서, 여러 프로세스가 공유 자원을 상호 배타적으로 사용하기 위해서는 자바의 Lock으로는 처리가 불가능하다. 이를 가능하게 해주는 락이 distributed lock이다. Spring integration에서 구현할 수 있는 distributed lock에는 대표적으로 JDBC와 redis를 이용하는 방법이 있다. Spring Integration을 이용한 Redis Lock 사용해보기 의존성 추가 <dependency> <groupId>org....

2024-09-15 · 2 min · 280 words

Redirection( , )과 Pipe( ) 차이

Redirection은 (>의 경우) 왼쪽 프로그램의 출력을 오른쪽으로 지정한다. 파일 등으로 지정할 수 있다. pipe는 왼쪽 프로그램의 output을 오른쪽 프로그램의 input으로 사용한다.

2024-09-15 · 1 min · 21 words

Record Lock

목표 record lock이 무엇인지 이해한다. 목적 index에 걸리는 락 SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE이고 c1에 대해서 인덱스가 걸려있는 경우에, 다른 트랜잭션에서 t.c1 = 10 인 값을 수정하지 않도록 막아야된다. UPDATE 쿼리를 수행할 때 c1 같은 secondary index로 조건을 건다면, innoDB에서 secondary index로 PK를 찾고, PK를 통해서 행을 조회하므로, c1 = 10과 그에 해당하는 모든 PK에 record lock이 걸린다. 참고 자료 https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-record-locks

2024-09-15 · 1 min · 66 words