equals를 재정의 하고 싶다면 일반 규약을 지켜야 한다 일반 규약은 다음과 같다 1. 반사성, reflexibility x가 null이 아닐 때 모든 x에 대하여 x.equals(x)는 true 2. 대칭성, symmetry null이 아닌 두 값 x, y가 있을 때, x.equals(y) == y.equals(x)가 true라면 그 반대도 성립해야 한다 3. 추이성, transitivity null이 아닌 세 값 x, y, z가 있을 때, x.equals(y) == true, y.equals(z) == true면 x.equals(z) == true 4. 일관성, consistency x, y의 값이 변하지 않을 때 x.equals(y) == true라면 언제 호출하더라도 false로 변해선 안 된다..
Spring + JPA 사용 시 JPA 구현체 중 하나인 Hibernate가 제공하는 꿀 기능이 있다 show_sql, format_sql 두 속성인데 sql을 정렬된 상태로 볼 수 있게 해 준다 여기에 logging 하위 속성이 제공하는 BasicBinder까지 사용하면 sql parameter에 무슨 값이 들어가는지 알 수 있다 단 이전 글에서도 언급했듯 기본 제공하는 것을 그대로 쓴다면 큰 단점이 있다 sql 쿼리가 점점 복잡해질수록 가독성이 좋지 못하고 조건이 늘어날수록 어떤 값이 어떤 변수에 붙는지 알기 힘들다 또한 ordinal로 보여주기 때문에 column=value 형식이 아닌 [0]=value 형식으로 보게 된다 [P6Spy] Log4j2, p6spy 적용해보기 진행하고 있는 프로젝트에서..
Item09는 try-finally 보다 try-with-resources를 사용하라 말한다 당연하다, try-finally로 자원 반환을 직접할 필요가 없다 한 때는 try-finally로 자원을 직접 닫고 메서드를 끝내던 시기가 있었다 try-finally로 명시해 자원을 반환하던 이는 지겨운 반복 작업에서 벗어나고자 try-with-resources를 개발했을 것이다 그렇다면 자바를 사용하는 우리와 같은 입장에서는 무조건 쓰는 것이 좋다 다른 API는 트레이드오프라도 있지만 try-with-resources 만큼은 절대적이다 그냥 쓰자 써야할 이유를 굳이 더 붙이자면 try-finally 사용 시에 try 절에서 잘못된 파일을 열어 예외가 발생했는데 finally 절에서 파일을 닫기 위해 file...
Item08의 핵심은 자바에서 제공하는 두 가지 객체 소멸자 finalizer, cleaner를 사용하지 말라는 것이다 핵심을 충실히 따라 앞으로도 모르고 지내도록 하자 여기서 글을 끝마칠 수도 있는 내용이지만 왜인지 한번 알아보자 이제는 당연해져 버렸다 밸덩부터 읽고 시작하자 https://www.baeldung.com/java-finalize finalizer, cleaner라는 메서드가 존재하는 게 아니라 사실 finalize(), clean() 메서드를 의미한다 C++에서는 개발자가 메모리를 직접 다루기 때문에 객체 소멸자도 중요한 의미를 갖지만 자바는 다르다 GC가 우리가 치워야 할 똥을 대신 치워주고 try-finally 문으로 명시적으로 자원을 닫아줄 수 있으며 보다 발전된 try-with-..
OAuth2 로그인을 하더라도 애플리케이션에서 회원 관리를 직접 하고 있다면 CustomOAuth2UserService를 만들어 우리의 DB에 OAuth2 로그인으로 받아온 회원 정보를 넣어줘야 한다 그렇다면 그 설정은 어찌하는가? 흔히 사용하는 SecurityConfig에서 http를 인자로 받는 메서드에서 설정을 잡아준다 수많은 튜토리얼들이 있으니 참고해보시고 최신 버전의 스프링부트 스타터들을 사용한다면 여기까지 설정했을 때 구글 로그인은 customOAuth2UserService를 타지 않는다 왜 내가 만든 커스텀 서비스를 안 타는지 이해 하려면 OAuth2, OIDC (open id connect) 두 방식의 차이를 이해해야 한다 OAuth2는 인가에 초점을 맞췄고 다양한 상황에 쓰일 수 있도록 ..
입사 전부터 꽤나 꾸준히 개인 프로젝트나 인프런 강의 같은 걸로 독학을 해왔다 개인적인 생각으로 알고리즘은 코테 준비를 위해서는 어쩔 수 없이 해야겠지만 시스템 / 네트워크 개발자가 아니라면 지금 당장 시작해야 되는 건 아니라고 생각한다 특히 프론트엔드 & 백엔드 개발자라면 그 보다 시급하게 공부해야 할 것들이 차고 넘치니 근데 알고리즘 외 CS는 다르다고 생각한다 우리가 작성한 프로그램이 어디서, 어떻게 실행되고 누구와 통신하는지 알면 알수록 이해의 깊이가 달라진다 웹 개발자인데 HTTP가 무엇인지 모르고 자바/스프링, 파이썬/장고, 자스/노드 같은 것만 가지고 이해 없이 프레임워크가 제공하는 API만 사용해 프로젝트 만드는 건 사실 단순노동에 불과하다 그럼에도 불구하고 나도 그랬고, 초보 입장에서는 ..
자바는 참 편하다 개발자가 신경 쓰지 않아도 메모리 관리를 척척 해주고 write once, run anywhere 정신으로 각각의 운영체제 별로 코드를 따로 작성하지 않아도 된다 JVM 덕분에 편한 인생을 살고 있지만 그럼에도 메모리 관리를 직접 해줘야 하는 경우도 있다 1. 책의 예시에서 나오는 자기 메모리를 직접 관리하는 Stack과 같은 클래스 작성 시 2. 애플리케이션 내의 캐시 이용 시 3. 리스너 / 콜백을 이용하는 이벤트 기반으로 작성 시 대부분의 상황에서는 JVM이 우리의 할 일을 대신하여 메모리라는 방을 청소해준다 JVM이 해주지 못하는 예외의 경우가 위 세가지 상황 같은 것이고 제대로 처리하지 않으면 메모리 누수로 연결된다 다 쓴 참조를 가지고 있는 경우 더 이상 사용할 일이 없어도 ..
자바가 오라클로 인수되면서 저물어 간다는 여론도 많고 자바스크립트의 떡상으로 위상이 많이 떨어지긴 한 것 같다 자바 사용자 입장에서는 가슴 아픈 일이 아닐 수 없다 근데 난 자바의 요즘 행보를 보면 실상은 다르다고 생각한다 벌써 나온지 8년 된 자바 세계의 혁명이었던 자바 8부터 FP를 강력히 지원하기 위해 스트림, 람다가 도입되고 인터페이스에 default 메서드를 넣을 수 있어 하위 호환성을 유지하며 확장해 갈 수 있게 됐다 이후에도 버전업 하면서 코틀린의 data class에 해당하는 record, switch case 문을 간편하게 사용할 수 있는 pattern matching도 나오고 한정적 상속을 가능케 하는 sealed 키워드 등등 자바도 계속해서 발전하고 있다 실무에서는 아무래도 언어 자체..
Item06의 핵심은 쓰잘데기 없는 객체를 생성하지 말라는 것이다 대표적인 예로 정규표현식이 있다 String의 matches 메서드를 이용하면 str 문자열이 정규표현식에 맞는지 비교해 boolean 값을 반환한다 내부 구현이 궁금하다면 String.matches -> Pattern.matches -> Pattern.compile -> Pattern()을 따라가 보자 String.matches 에서 문제가 되는 부분은 new Pattern(regex, 0) 부분이다 쓸데없이 Pattern 객체가 생성된다 정규표현식을 사용해야 하는 데이터가 매우 많은데 매번 String의 matches를 호출하면 어떻게 될까? 프로그램이 터지는 건 아니고 좀 느려진다, 그럼에도 불구하고 다른 방법을 사용해야 한다 더 좋..
Item05는 자원을 때려 박지 말고 외부에서 주입받을 수 있도록 만들라는 것이 주요 내용이다 한 줄로 써놔서 간단해보이지만 객체지향의 핵심이라 할 수 있다 개인이 진행하는 토이 프로젝트가 아니라면 첫 출시와 비교해서 달라지지 않을 프로젝트는 없을 것이다 모두가 만족해할 기능은 없을 것이고 특정 기능에 다수가 만족하더라도 소수는 싫어할 수도 있다 마음 넓은 프로젝트는 구닥다리 IE의 낮은 버전까지도 지원해줘야 하고 그들만을 위한 기능도 추가해줘야 한다 계속해서 변화하고, 기존 코드에 추가되어야 할 코드가 잔뜩 있는데 기존 코드를 바꿀 수 없다면 어떻게 될까? 극단적인 예시지만 때려 박는 코드에선 현실이 된다 하위 호환성까지 알뜰살뜰 챙기는 자바에서 타격이 더욱 크다 공개 API는 쉽게 바꿀 수 없다 더 ..
[AOP] 소요 시간 측정 AOP class level에 적용하기 진행 중인 프로젝트 메서드에 slow query 분석 또는 잘못된 비즈니스 로직을 해결하기 위해 시간 측정 AOP를 활용해봤다 우선 aop 관련 package를 따로 만들어주고 시간 측정할 메서드에 적용시킬 custom ryumodrn.tistory.com 이전 글에 해당하는 내용 지난 버전에서는 LogExecutionTime이라는 custom annotation을 만들고 이 녀석을 적용시킬 수 있는 범위를 클래스까지 추가하여 클래스에 해당 애노테이션을 붙여 시간 측정을 했다 실제 운영 시에는 이와 같이 선별적인 방식으로 시간 측정할 클래스에 애노테이션을 붙여 사용해야 할 것인데 애노테이션을 붙이지 않고 더 간편하게 선별적으로 적용시키는 ..
프로젝트를 진행하다 보면 문자열, 숫자 상수만을 담은 Constant 클래스를 작성할 때가 종종 있다 다양한 클래스에서 사용할 수 있는 Utility 클래스도 마찬가지다 두 클래스의 목적은 특정 클래스에 속하지 않으면서 프로젝트 전체에서 유용하게 사용할 수 있는 코드를 일반화하는 것이다 상수 클래스의 경우 public static final로 한 번만 초기화된 상태로 변경할 수 없도록 해두고 유틸리티 클래스의 경우 public static 으로 접근을 허용하며 메서드를 가진 클래스의 인스턴스화 없이도 사용하게 한다 따라서 두 클래스 모두 인스턴스화될 필요가 없으니 생성자를 private으로 두어 상속 / 인스턴스화를 할 수 없게 해야 한다 사실 상수, 유틸리티 클래스는 인스턴스화 될 필요가 없고 되면 낭..
싱글턴이란 애플리케이션 전체에서 단 하나만 존재하는 인스턴스를 의미한다 그러면 왜 단 하나만 필요할까? 크게 보면 두 가지 이유가 있다 1. 함수형 프로그래밍에서 자주 사용되는 불변 객체로 사용하기 위해 2. 생성 비용이 너무 비싸고, 스레드 간 공유해도 안전한 컴포넌트인 경우 1번은 Java Web Application을 만들 때 흔히들 사용하는 DTO 형태에서 상태 변경만 하지 못하도록 하면 된다 2번은 JPA에서 사용되는 EntityManagerFactory와 같은 객체를 예로 들 수 있다 EntityManagerFactory는 스레드 간 공유해도 안전하며 생성 비용이 크다 싱글턴을 만드는 방식은 어떻게 될까? 1. 생성자를 private으로 만들고 인스턴스 반환은 static method를 이용한..
인스턴스 생성할 때 받아야 하는 매개변수가 무지막지하다면 어떻게 해야 할까? 1. telescoping constructor pattern 2. java beans pattern 3. builder pattern 점층적 생성자 패턴은 받아야 하는 생성자 오버로딩을 통해 매개변수 수를 늘려가는 것이다 매개변수 개수에 따라 계속 늘어나야 하므로 유지보수까지 생각한다면 지옥이나 다름없다 boiler plate가 차지하는 코드가 도메인에서 필요한 필드, 메서드 보다 더 많아질 수 있다 Java Beans Pattern 은 기본 생성자를 하나 두고 setter로 필드 값을 지정해준다 하나의 instance 만들려면 setter 난무해야 하고 필수 값을 빠뜨리고 생성할 수도 있기 때문에 일관성이 보장되지 않는다 s..
최근 이펙티브 자바 3판 1 회독을 끝냈다 다른 사람들의 리뷰, 후기를 보면 책 수준이 굉장하다는데 나도 동감한다 개인적으로 책을 볼 때 1회독은 빠르게 흐름을 잡고 2-3 회독하면서 깊게 이해하는 편이라 야무진 책 내용을 기억에 오래 붙잡아두기 위해 정리해보고자 한다 책 내용을 훑는데 그치지 않고 한 발자국 더 나아가 개인적인 의문점에 대해 찾아보고 정리해야겠다 아이템 1의 핵심 내용은 new 연산자를 사용해 클래스를 생성하는 코드 대신 static factory method를 사용하라는 것이다 왜 그래야 할까? 장점은 다음과 같다 1. new Class() 대신 이름을 가진 메서드로 호출할 수 있다 2. 호출될 때마다 인스턴스를 생성할 필요가 없다 3. 반환 타입의 하위 타입 객체를 반환할 수 있다 ..
이 글은 OAuth2 구현을 소개하는 글은 아니다, SpringBoot OAuth2 구현은 이미 훌륭하게 작성된 예제 코드가 많다 구글에 검색했을 때 대부분의 OAuth2 구현은 callicoder.com에서 소개된 예제를 기반으로 만들어져 있다 나도 callicoder.com을 참고해 OAuth2를 구현하였고 코드를 자세하게 이해하기 위해, 또한 가독성 향상을 위해 여러 번의 리팩토링을 거쳐서 코드는 조금 다를지라도 흐름은 거의 비슷하다 아래 블로그는 한국어로 친절하게 설명되어 있어 구현하는데 어려움이 없을 것이다 https://www.callicoder.com/spring-boot-security-oauth2-social-login-part-2/ http://yoonbumtae.com/?p=3000 ..
아래 쿼리는 최근 개정된 Real MySQL 2, 11.4.10.3.1 동등 또는 크다 작다 비교에 해당하는 부분이다 오늘 만난 문제는 format = tree 부분에서 json, traditional은 되는데 tree는 인식이 안 되는 것이다 나는 mysql을 docker에 깔아서 사용하는 중이고 latest로 받아서 8.0.26을 사용하고 있다 나와 같은 문제를 겪고 있다면 우선 자신이 사용하는 CLI, GUI를 이용해 버전을 확인해보자 select version(); 공식 레퍼런스에는 대놓고 format_name에 TREE가 존재하는데 왜 안되는 걸까? explain format = tree로 여러 번 검색을 해보다가 explain analyze 키워드를 알게 되었고 적용해봤더니 tree 형태의 실..
[Redis] HATEOAS와 @Cacheable - 2 [Redis] HATEOAS와 @Cacheable 이전 Redis 삽질기에서 해결법은 찾지 못 했으나 타협점을 찾았기 때문에 올린다 내가 의도 했던 것은 RestController 응답을 HATEOAS에 맞춰 ResponseEntity 또는 ResponseEntity.. ryumodrn.tistory.com 위 글에서 이어지는 글이다 2편에서의 문제점은 GenericJackson2JsonRedisSerializer를 사용했을 때 발생하던 아래 에러다 Caused by: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class org.springframewo..
[Redis] HATEOAS와 @Cacheable 이전 Redis 삽질기에서 해결법은 찾지 못 했으나 타협점을 찾았기 때문에 올린다 내가 의도 했던 것은 RestController 응답을 HATEOAS에 맞춰 ResponseEntity 또는 ResponseEntity 로 내려주려고 했다 또한 응답. ryumodrn.tistory.com 위 글에서 이어지는 글이다 많은 우여곡절 끝에 HATEOAS를 적용한 PagedModel 형태에 캐시 추상화 @Cacheable을 접목했다 정확하게 말해 Page를 캐시하고 Controller 단에서 Page 형태의 캐시 데이터를 PagedModel로 변환하여 반환한다 지난 글에서의 문제점은 _links 가 깨진다는 점이었다 사실 위 문제를 해결하고도 PagedModel에..
[Redis] @Cacheable 삽질기 조회 성능 향상을 위해 Spring Data Redis를 도입했고 간단해 보이는 @Cacheable, @CachePut 등을 사용하기로 결정했다 그런데 LocalDateTime을 변환할 때 살짝 문제가 있었다 가장 쉬운 해결 방법은 아래와 같 ryumodrn.tistory.com 위 글에서 이어지는 글이다 해결은 금방 했는데 이제야 올리게 됐다 Redis를 사용하면서 @Cacheable로 데이터를 캐싱할 때 발생했던 문제다 LocalDateTime을 JSON 형태로 직렬화 / 역직렬화할 때 LocalDateTime이 풀어 헤쳐지던 문제인데 해결은 간단했다 @JsonFormat(pattern=어쩌구, timezone=저쩌고)로도 풀 수 있지만 이 애노테이션을 모든 ..