2023-02-17-TIL
2023-02-17-TIL
Today I learned
EOF
EOF(end-of-file)는 데이터 소스에서 더 읽을 데이터가 없는 상태를 말한다. 파일 끝에 특별한 문자가 저장되어 있는 것이 아니라 읽기 함수가 돌려주는 신호다. C 표준 라이브러리에서는 getchar 같은 함수가 음수인 EOF 매크로 값(보통 -1)을 반환해 이를 알리고, 자바의 InputStream.read()도 -1을 반환한다. 터미널 입력에서는 Unix 계열의 Ctrl+D처럼 정해진 키 입력을 EOF로 해석한다. JSON 파서가 “입력이 예상보다 일찍 끝났다”는 오류를 내는 것도 같은 개념으로, 빈 요청 본문이나 잘린 JSON을 읽을 때 생긴다.
- https://en.wikipedia.org/wiki/End-of-file
- https://stackoverflow.com/questions/31155914/what-is-eof-and-what-is-its-significance-how-can-it-be-noticed
- https://www.computerhope.com/jargon/e/eof.htm
- https://www.quora.com/What-is-EOF-and-what-is-its-significance-How-can-it-be-noticed
- https://javarevisited.blogspot.com/2018/01/how-to-ignore-unknown-properties-parsing-json-java-jackson.html#axzz7tX6yWqHe
@JsonIgnoreProperties
Jackson 애너테이션으로, 직렬화할 때 특정 프로퍼티를 빼거나 역직렬화할 때 특정 JSON 프로퍼티를 무시한다. @JsonIgnoreProperties({"internalId", "secretKey"})처럼 이름을 나열하거나, @JsonIgnoreProperties(ignoreUnknown = true)로 클래스에 대응하는 setter나 생성자가 없는 모든 프로퍼티를 예외 없이 무시하게 할 수 있다. Javadoc에 따르면 ignoreUnknown은 역직렬화에만 영향을 준다. 외부 API 응답처럼 필드가 늘어날 수 있는 JSON을 받을 때 UnrecognizedPropertyException을 피하려고 쓴다. 전역으로는 ObjectMapper의 FAIL_ON_UNKNOWN_PROPERTIES를 끄는 방법도 있다.
- https://fasterxml.github.io/jackson-annotations/javadoc/2.9/com/fasterxml/jackson/annotation/JsonIgnoreProperties.html
- https://www.geeksforgeeks.org/how-to-ignore-unknown-properties-while-parsing-json-in-java/
- https://stackoverflow.com/questions/19973762/jackson-wrapper-deserialization-best-practices
- https://javarevisited.blogspot.com/2018/01/how-to-ignore-unknown-properties-parsing-json-java-jackson.html#axzz7tX6yWqHe
- https://velog.io/@hth9876/JsonIgnorePropertiesignoreUnknown-true
Assertion Frameworks: AssertJ vs JUnit
JUnit의 assertEquals(expected, actual)는 인자 순서를 헷갈리기 쉽고, 검증 종류마다 다른 정적 메서드를 찾아야 한다. AssertJ는 assertThat(actual).isEqualTo(expected)처럼 실제 값에서 출발하는 메서드 체인이라 IDE 자동완성으로 그 타입에 맞는 검증을 고를 수 있다. AssertJ는 JUnit 단언문을 AssertJ로 바꾸는 변환 스크립트도 제공하고, 두 방식이 한 프로젝트에 공존해도 된다고 안내한다. Google의 Truth도 비슷한 체인 방식인데, Truth 문서는 AssertJ가 없던 시절에 시작했기 때문에 따로 만들었다고 설명하고, Hamcrest는 매처 조합이 유연한 대신 제네릭이 복잡하고 실패 메시지가 읽기 어렵다고 비교한다.
- https://joel-costigliola.github.io/assertj/assertj-core-converting-junit5-assertions-to-assertj.html
- https://pjh3749.tistory.com/241
- https://stackoverflow.com/questions/2966347/assert-vs-junit-assertions
- https://annaduldiier.medium.com/assertj-vs-junit-483b7d6dc997
- https://effortguy.tistory.com/125
- https://www.xenonstack.com/insights/what-are-assertion-frameworks
- https://dzone.com/articles/hamcrest-vs-assertj-assertion-frameworks-which-one
- https://truth.dev/comparison.html
- https://loopstudy.tistory.com/287
Functional Programming in Java
Baeldung 글은 함수형 프로그래밍의 뿌리를 Alonzo Church의 람다 대수에 두고, 핵심 개념으로 일급 함수와 고차 함수, 순수 함수, 불변성, 참조 투명성을 든다. 자바는 Haskell 같은 순수 함수형 언어가 아니라 함수형과 절차형을 모두 허용하는 언어이고, Java 8의 람다와 함수형 인터페이스, 스트림으로 함수형 스타일을 쓸 수 있게 되었다.
- https://www.baeldung.com/java-functional-programming
- https://jenkov.com/tutorials/java-functional-programming/index.html
- https://www.geeksforgeeks.org/functional-programming-in-java-with-examples/
- https://leejaedoo.github.io/designing_with_lambda_expressions/
- https://www.scaler.com/topics/java/functional-programming-in-java/
- https://www.youtube.com/watch?v=VRpHdSFWGPs
First-Class Functions in Java
함수를 값처럼 변수에 담고, 인자로 넘기고, 반환할 수 있으면 그 언어는 일급 함수를 지원한다고 한다. 자바의 메서드 자체는 값이 아니지만, Java 8부터 람다 표현식과 메서드 참조로 “동작”을 함수형 인터페이스 타입의 객체로 만들어 넘길 수 있다. Spring 블로그가 소개한 『Java 8 in Action』 영상도 람다를 다른 메서드에 인자로 넘길 수 있는 코드 조각으로 설명한다. 엄밀히는 함수가 아니라 인터페이스를 구현한 객체이므로, Stack Overflow 답변들은 자바가 일급 함수를 “흉내” 낸다고 표현한다.
- https://ericnormand.me/podcast/what-are-first-class-functions
- https://dzone.com/articles/java-lambda-expressions-functions-as-first-class-citizens
- https://www.tutorialspoint.com/functional_programming_with_java/functional_programming_with_java_firstclass_function.htm
- https://stackoverflow.com/questions/22530345/does-java-8-support-functions-as-first-class-objects
- https://www.youtube.com/watch?v=Rd-sqHjmfB0
- https://rosettacode.org/wiki/First-class_functions
- https://spring.io/blog/2014/03/20/manning-publications-first-class-functions-in-java-8
Pure Functions in Java
순수 함수는 인자에만 의존해 값을 반환하고 부수 효과가 없는 함수다. 같은 입력에 항상 같은 결과를 내므로 테스트하기 쉽고, 호출을 결과값으로 바꿔도 의미가 같다(참조 투명성). Baeldung은 자바가 캡슐화된 객체의 상태를 메서드로 바꾸는 것을 기본으로 하므로 자바 메서드 대부분은 엄밀한 순수 함수가 아니라고 지적한다. 계산 로직을 상태 변경과 분리해 순수한 부분을 늘리는 것이 현실적인 목표다.
- https://blog.knoldus.com/functional-java-understanding-pure-functions-with-java/
DTO Inner Class
Oracle Java Tutorial의 용어로 static이 아닌 중첩 클래스는 inner class, static으로 선언한 것은 static nested class다. inner class는 바깥 클래스 인스턴스에 묶여 있어서 바깥 인스턴스 없이 만들 수 없고, 바깥 객체의 참조를 숨은 필드로 가진다. 그래서 요청과 응답 DTO를 한 클래스 안에 모을 때는 static nested class로 선언해야 Jackson이 바깥 인스턴스 없이 생성할 수 있다. 하나의 DTO를 여러 요청과 응답에 재사용할지, 용도마다 따로 둘지는 Stack Overflow 질문처럼 명시성과 클래스 개수 사이의 절충이다.
- https://velog.io/@nomonday/Spring-API-%EA%B0%9C%EB%B0%9C-%EC%8B%9C-request%EB%8A%94-DTO%EC%97%90-%EB%B0%9B%EC%95%84%EC%98%A4%EC%9E%90
- https://jiwondev.tistory.com/251
- https://unluckyjung.github.io/dev/2022/02/20/Dto-InnerClass/
- https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
- https://www.baeldung.com/java-dto-pattern
- https://stackoverflow.com/questions/44349199/reusing-dto-for-various-request-response-types-vs-explicitness-of-what-is-requir
Package by Component
Simon Brown이 『Clean Architecture』의 “빠져 있는 장(The Missing Chapter)”에서 제안한 패키지 구성이다. 기능별 패키지(package by feature)와 비슷하게 하나의 컴포넌트에 관련된 것을 모으되, 컴포넌트의 공개 API만 public으로 두고 저장소 구현 같은 내부는 패키지 접근 제어로 숨긴다. ttulka의 글에 따르면 이렇게 하면 컨트롤러가 서비스(비즈니스 로직)를 건너뛰고 저장소에 바로 접근하는 일을 컴파일러가 막아 준다. 아키텍처 규칙을 문서가 아니라 접근 제어자로 강제한다는 점이 핵심이다.
- https://medium.com/react-native-training/lets-write-examples-with-package-by-feature-approach-on-react-native-7494b49e034c
- https://blog.ttulka.com/package-by-component-with-clean-modules-in-java/
- https://medium.com/@mammimia/clean-architecture-part-iv-component-principles-89d3cb58c195
- https://stackoverflow.com/questions/46884449/uncle-bobs-clean-architecture-approach-what-is-recommended-package-structure
- https://www.baeldung.com/spring-boot-clean-architecture
- https://www.educative.io/blog/clean-architecture-tutorial
- https://github.com/serodriguez68/clean-architecture/blob/master/part-4-component-principles.md
- https://learning-notes.mistermicheels.com/architecture-design/reference-architectures/package-by-feature-or-component/
- https://docs.oracle.com/cd/E19683-01/806-7008/6jftmsc10/index.html
- https://dzone.com/articles/package-component-and
Google API
클라이언트 라이브러리 없이 Google API를 JSON REST로 직접 호출하는 방법을 설명한 문서다. 대부분의 Google API는 Discovery Service가 제공하는 Discovery Document라는 JSON 형식으로 정의되어 있고, 같은 API를 Protocol Buffers 기반 RPC로도 제공한다. API 설계 관점에서는 리소스 중심 URL과 표준 메서드를 일관되게 쓰는 Google API 스타일을 참고할 수 있다.
- https://googleapis.github.io/HowToREST.html
@ControllerAdvice vs @RestControllerAdvice
@RestControllerAdvice는 Javadoc 설명대로 @ControllerAdvice와 @ResponseBody를 합친 단축 애너테이션이다. @RestController가 @Controller와 @ResponseBody의 조합인 것과 같은 관계다. 따라서 @RestControllerAdvice 안의 @ExceptionHandler가 반환한 객체는 뷰 이름이 아니라 응답 본문으로 직렬화된다. REST API라면 @RestControllerAdvice를 쓰면 된다.
- https://stackoverflow.com/questions/43124391/restcontrolleradvice-vs-controlleradvice
- https://chanos.tistory.com/entry/Spring-%EC%A0%84%EC%97%AD-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-ControllerAdvice%EC%99%80-RestControllerAdvice
- https://velog.io/@banjjoknim/RestControllerAdvice
- https://www.bezkoder.com/spring-boot-restcontrolleradvice/
@ExceptionHandler and @ControllerAdvice
@ExceptionHandler는 처리할 예외 타입을 지정한 메서드이고, 컨트롤러 안에 두면 그 컨트롤러의 예외만 처리한다. @ControllerAdvice 클래스에 두면 여러 컨트롤러에 공통으로 적용된다. 컨트롤러마다 같은 핸들러를 반복하지 않아도 되고 예외 처리 정책을 한곳에서 관리할 수 있다는 것이 @ControllerAdvice의 장점이다. 컨트롤러 안의 핸들러가 전역 핸들러보다 먼저 적용된다.
- https://tecoble.techcourse.co.kr/post/2021-05-10-controller_advice_exception_handler/
- https://incheol-jung.gitbook.io/docs/q-and-a/spring/controlleradvice-exceptionhandler
- https://stackoverflow.com/questions/35323174/what-are-the-advantages-of-controlleradvice-over-exceptionhandler-or-handlerex
- https://www.digitalocean.com/community/tutorials/spring-mvc-exception-handling-controlleradvice-exceptionhandler-handlerexceptionresolver
- https://www.bezkoder.com/spring-boot-controlleradvice-exceptionhandler/
- https://samtao.tistory.com/42
Error Message Management
에러 메시지를 코드 곳곳의 문자열 리터럴로 두지 않고 한곳에서 관리하는 방법이다. 에러 코드 enum에 메시지를 함께 두거나, messages.properties와 스프링의 MessageSource로 분리해 다국어 메시지까지 관리하는 방식이 대표적이다.
- https://hyune-c.tistory.com/entry/ErrorMessage%EB%A5%BC-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95
Spring Boot Exception Handling
실제 프로젝트의 예외 처리 코드를 모아 본 것이다. Toptal 글은 ResponseEntityExceptionHandler를 상속한 RestExceptionHandler에서 스프링 MVC가 던지는 표준 예외의 handle...() 메서드를 재정의하고, 상태, 메시지, 디버그 메시지, 하위 에러 목록을 담은 ApiError로 응답을 통일한다. GitHub 예제들도 EntityNotFoundException 같은 도메인 예외를 정의하고 전역 핸들러에서 상태 코드로 변환하는 같은 구조를 따른다.
- https://github.com/brunocleite/spring-boot-exception-handling/blob/master/src/main/java/com/example/springbootexceptionhandling/EntityNotFoundException.java
- https://www.toptal.com/java/spring-boot-rest-api-error-handling
- https://github.com/cami-la/exceptions-java/blob/master/src/br/com/dio/exceptions/UncheckedException.java
- https://github.com/stormpath/spring-mvc-rest-exhandler/blob/master/main/src/main/java/com/stormpath/spring/web/servlet/handler/RestExceptionHandler.java
- https://github.com/jirutka/spring-rest-exception-handler/tree/master/src/main/java/cz/jirutka/spring/exhandler/handlers
Dynamic vs Static vs SPA
Academind 글은 웹사이트를 HTML이 만들어지는 위치로 나눈다.
- Dynamic: 요청마다 서버가 서버 사이드 언어와 템플릿 엔진으로 HTML을 만든다. 사용자별 데이터를 담기 쉽다.
- SPA: 서버는 미리 만든 HTML 한 장만 주고, 그 안의 JavaScript가 브라우저에서 화면을 바꾼다. 데이터는 API로 받아 온다.
Static: 정적 사이트 생성기로 미리 만든 HTML 파일을 그대로 서빙한다. 빠르고 호스팅이 싸지만 요청마다 다른 내용을 만들 수는 없다.
- https://academind.com/tutorials/dynamic-vs-static-vs-spa
- https://betterprogramming.pub/web-development-in-2021-dynamic-vs-static-vs-single-page-architecture-399d0c3defe6
@Async in Spring
@EnableAsync를 켜고 메서드에 @Async를 붙이면 호출이 별도 스레드에서 실행된다. 반환값이 필요하면 Future나 CompletableFuture로 받는다. @Async가 동작하지 않는 흔한 이유는 프록시 방식이기 때문이다. Spring 문서는 기본 프록시 모드에서는 프록시를 거치는 호출만 가로챌 수 있고, 같은 클래스 안의 내부 호출은 가로챌 수 없다고 설명한다. 그래서 같은 빈 안에서 this.asyncMethod()로 부르면 그냥 동기로 실행된다. @PostConstruct 같은 생명주기 콜백과도 함께 쓸 수 없다.
- https://velog.io/@gillog/Spring-Async-Annotation%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
- https://goodgid.github.io/SpringBoot-Why-doesn’t-it-work-with-Async/
- https://steady-coding.tistory.com/611
Spring 6.0 and Spring Boot 3.0
Spring Framework 6.0은 2022년 11월 GA로 나왔다. 발표 글에 따르면 Java 17 이상이 기본 요구 사항이 되었고, Jakarta EE 9 이상(javax 대신 jakarta 네임스페이스)으로 옮겨 가며 Servlet 6.0, JPA 3.1 같은 Jakarta EE 10 API에 맞췄다. AOT(Ahead-Of-Time) 변환 기반이 들어와 Spring Boot 3에서 GraalVM 네이티브 이미지를 정식 지원하고, HTTP 인터페이스 클라이언트, RFC 7807 Problem Details 지원, Micrometer 기반 관측성 같은 기능이 추가되었다. 업그레이드할 때 가장 손이 많이 가는 부분은 javax.* 패키지를 jakarta.*로 바꾸는 일과 Spring Security 설정 변경이다.
- https://spring.io/blog/2022/11/16/spring-framework-6-0-goes-ga
- https://docs.spring.io/spring-security/reference/migration/index.html
- https://www.baeldung.com/spring-boot-3-spring-6-new
- https://github.com/spring-projects/spring-framework/wiki/What’s-New-in-Spring-Framework-6.x
- https://marrrang.tistory.com/m/101?category=925235
- https://www.springcloud.io/post/2022-02/springframework-6/#gsc.tab=0
- https://springframework.guru/getting-ready-for-spring-framework-6/
- https://www.infoq.com/news/2021/09/spring-6-spring-boot-3-overhaul/
- https://www.youtube.com/watch?v=TR254zh-f3c
- https://www.youtube.com/watch?v=mitWK_DwKGs
Multi-Threading in Spring Boot Using CompletableFuture
@Async 메서드가 CompletableFuture를 반환하게 하고, 호출하는 쪽에서 여러 결과를 CompletableFuture.allOf()로 기다렸다가 합치는 패턴이다. 독립적인 외부 호출 여러 개를 동시에 실행해 전체 응답 시간을 줄일 때 쓴다. 실행 스레드 풀은 ThreadPoolTaskExecutor 빈으로 직접 지정해 두는 편이 안전하다.
- https://swathisprasad.medium.com/multi-threading-in-spring-boot-using-completablefuture-a7ca68a0fe48
Spring Data JPA and R2DBC
JPA는 JDBC 위에서 동작하는 블로킹 API라서 WebFlux 같은 리액티브 스택에서 쓰면 이벤트 루프 스레드를 막는다. Stack Overflow 질문 제목처럼 JPA는 리액티브 저장소를 지원하지 않는다. Spring Data는 2016년 글에서 ReactiveCrudRepository처럼 리액티브 타입을 반환하는 저장소 모델을 소개했고, 관계형 DB에서는 리액티브 드라이버 규격인 R2DBC가 이 역할을 맡는다. 한 애플리케이션에서 JPA와 R2DBC를 함께 쓰는 것도 가능하지만, 트랜잭션 매니저와 설정이 둘로 나뉘는 점을 감수해야 한다. Kotlin에서는 코루틴으로 리액티브 코드를 동기 코드처럼 쓸 수 있다.
- https://jiwhunkim.github.io/develop/2020/02/01/spring-using-together-jpa-and-r2dbc.html
- https://stackoverflow.com/questions/52480271/spring-webflux-jpa-reactive-repositories-are-not-supported-by-jpa
- https://spring.io/blog/2016/11/28/going-reactive-with-spring-data
- https://jyeonjyan.tistory.com/m/5
- https://www.baeldung.com/spring-mvc-async-vs-webflux
- https://www.baeldung.com/kotlin/spring-boot-kotlin-coroutines
Tomcat vs Jetty
둘 다 자바 서블릿 컨테이너다. Tomcat은 Apache 라이선스의 Apache 프로젝트로 Jakarta Servlet, Jakarta Server Pages 등 핵심 명세를 구현하고, Spring Boot가 기본 내장 서버로 쓴다. Jetty는 Eclipse 재단 프로젝트로, 비교 글은 부팅 시간과 메모리 사용량을 줄이는 데 우선순위를 두고 다른 시스템에 내장하기 쉽다는 점을 강점으로 든다. Spring Boot에서는 spring-boot-starter-web에서 Tomcat을 제외하고 spring-boot-starter-jetty를 추가해 바꿀 수 있다.
- https://cloudinfrastructureservices.co.uk/tomcat-vs-jetty-whats-the-difference/
댓글
아직 댓글이 없습니다