퍼사드 패턴이란?
복잡한 서브 시스템 의존성을 최소화하는 패턴
서브 시스템에 있는 일련의 인터페이스를 하나의 인터페이스로 통합한다.
고수준의 인터페이스로 정의하여 서브 시스템의 세팅 과정을 생략한다. 사용하기 편하다.
퍼사드 패턴은 외부 코드에 대한 의존성을 줄여준다. 특정 시스템 및 라이브러리에 대한 코드를 숨긴다.
서브 시스템의 의존성을 한 곳에 모아 관리하기 편하다.
다만, 퍼사드 클래스가 서브 시스템에 대한 모든 의존성을 가진다. 이로 인한 문제가 발생할 수 있다.
어댑터 패턴
데이터 타입을 변경하는 패턴
라이브러리나 프레임워크 등 외부 코드를 바로 사용하지 않는다. 외부 코드는 지정한 인터페이스에 맞춰 구현하고, 내부코드는 해당 구현체를 사용한다. 내부 코드와 외부 코드 간 의존성을 최소화 한다.
데이터 타입을 강제하여 일관된 인터페이스를 제공할 때도 사용한다. List, Object[], Map 등 다양한 집합 구조를 Iterator 인터페이스로 관계를 맺을 수 있다.
상태패턴
자신의 상태를 평가하고 행위를 정의하는 코드는 복잡하다. 왜냐하면 if절을 반복하여 사용하기 때문이다.
상태가 확장되면 이를 반영하기 위한 if절이 추가된다. 변경에 열려 있다.
상태패턴은 자신의 상태를 평가하고 수행하는 로직을 별도의 클래스에 위임한다.
구현은 전략 패턴과 유사하다.
전략 패턴이 자신의 전략을 interface 필드로 두는 것처럼 상태 패턴 역시 그러하다.
다만, 전략 패턴의 전략은 외부에서 주입된다. 상태 패턴의 상태는 객체 내부 혹은 상태 내부 로직에 의하여 결정된다.
옵저버 패턴
다수의 객체(observer)가 특정 객체(subject)의 상태 변화를 감지하고 알림을 받는 패턴.
옵저버가 서브젝트로부터 데이터를 가져(pull)오지 않는다. 서브젝트가 데이터를 변경하고 그 데이터를 옵저버에 보낸다(push).
subject와 observer 간 관계가 느슨하다.
observer는 subject가 어떤 데이터를 생산하고 전파하는지 알지 못한다. 그저 데이터를 주고 받는 관계만 형성한다.
새로운 observer가 추가되더라도 subject는 영향을 받지 않는다.
반복되는 호출로 인한 리소스를 최소화 한다. 옵저버는 서브젝트의 상태가 변경되었는지 주기적으로 호출하지 않아도 된다.
런타임 시점에서 옵저버를 추가하거나 제거할 수 있다.
다만, observer 객체를 등록한 후 해지 않는다면 memory leak이 발생할 수도 있다
서브젝트의 코드가 복잡해질 경우, 옵저버 패턴을 포기하고 push가 아닌 pull로의 변경을 고려한다.
템플릿 패턴 Template Pattern, 템플릿 콜백 패턴 Template Callback Pattern
템플릿 패턴 : 알고리즘의 구조를 서브 클래스가 확장할 수 있도록 템플릿으로 제공하는 방법.
템플릿 콜백 패턴 : 상속 대신 위임을 사용하는 템플릿 패턴.
템플릿 코드를 재사용하고 중복 코드를 줄일 수 있다.
구체적인 알고리즘만 변경할 수 있다.
proxy와 this간 불일치로 인한 aop 미적용
스프링 트랜잭션은 스프링 aop를 사용한다. 스프링 aop는 프록시 패턴을 사용한다. 스프링 어플리케이션이 로딩할 때, @Transactional을 선언한 객체의 프록시 객체를 생성하고, 프록시 객체를 빈에 등록한다.
우리는 스프링 컨텍스트에서 해당 객체를 호출한다고 생각하지만 사실은 프록시 객체가 호출되고 프록시 객체가 해당 객체를 호출한다. 그리고 프록시 객체에 트랜잭션과 관련한 코드가 작성된다. 이 말은 프록시 객체를 거쳐야지만 트랜잭션이 동작한다.
이와 같은 이해를 바탕으로 아래의 코드를 보자. 다음과 같은 상태에서 트랜잭션은 동작하지 않는다.
rest api와 view 렌더링 컨트롤러가 동시에 있을 때, 각각의 예외 어드바이스 적용이 안된다.
한 프로젝트에 rest api와 view 렌더링 컨트롤러 두 개가 있다. 가능하면 분리하면 좋을 테지만 일단은 한 프로젝트와 한 어플리케이션에 동작한다.
컨트롤러에 대한 ExceptionHandler를 @ControllerAdvice와 @RestControllerAdvice로 구현하였다.
그런데 문제가 발생했다. 모든 예외가 @RestControllerAdvice를 통해 동작하였다.
REST?
REST는 생각보다 오래된 개념이다. 2000년 Roy Fielding의 논문으로부터 시작된다. http 1.0 이후 새로운 소프트웨어의 아키텍쳐로서 REST가 제안된다. 참고로 로이필딩은 http 1.1의 주요 저자 중 한 명이다.
REST는 일종의 아키텍쳐이자 규칙이다. http 통신을 아래 6가지의 REST 원칙에 따라 구축하는 것이 주요 목표이다.
string constant pool
String은 string constant pool을 사용한다.
String은 객체지만 같은 문자열이 이미 존재하면 새로운 메모리에 등록하지 않고 기존에 있던 메모리를 가리키도록 한다.
그러므로 같은 문자열에 대해서는 기본적으로 동일하며 동등하다.
자바 컴파일과 JDK 버전 관련 옵션 오픈소스 등 다른 사람과의 공유를 목적으로 자바 프로젝트를 jar파일로 빌드할 때, 사용 가능한 JDK 버전을 최대한 낮은 버전으로 설정하는 것이 유리하다. jdk를 빌드할 경우 기본적으로 해당 버전에 맞춰 빌드한다. jdk를 최신 버전인 19로 사용하고 있고, 아무런 고민 없이 빌드한 jar 파일을 상대방에게 전달한 경우, 클라이언트 개발자는 jdk 19가 없으면 사용하지 못하는 불상사가 있을 수 있다. 호환성을 최대한 넓게 고려하면 8버전이 가장 낫다. 다만 var를 타입으로 선언하는 것을 포기해야 한다. Optional이나 Stream, Collections 등에서는 9버전 이상만 가능한 몇 가지 함수가 있다. 만약 해당 함수를 사용했다면 자바 8에 맞춰 다시 변경해야 한다. 이처럼 컴파일과 관련한 옵션을...
들어가며
스프링의 통합 테스트(@SpringBootTest)를 수행할 때 MockMVC를 주로 사용한다.
MockMvc에서 주로 사용하는 기능을 정리하고자 한다.
참고로 MockMvc에 사용하는 정적 메서드가 많다. 해당 import를 찾는 과정이 복잡하므로 보통 아래의 코드를 긁어서 사용한다. 사실 차후 내가 사용하기 위한 기록으로서 지금 블로그를 작성한다(^^;).
참고로 유사한 스태틱 메서드라 하더라도 Request와 Response 형태로 분리된다. 이 부분을 고려하여 테스트 코드를 작성하자!
join의 성능 문제
기존 프로젝트를 개선하는 과정에서 inner join으로 두 테이블을 합성해야 할 일이 있었다.
두 테이블은 특정 칼럼으로 연관관계를 가지고 있었다. 각 테이블의 연관관계 칼럼은 unique, pk, fk가 아니며, 인덱스가 존재하지 않았고, 최대 10개의 영문자와 숫자로 이뤄져 있었다.
첫 번째 테이블은 1만개 정도 레코드가 있었고 두 번째 테이블은 2천여개의 레코드가 있었다.
db는 mysql 5 버전이었다.
레코드의 양이 얼마 되지 않았으므로 성능 문제가 없을 것이라 생각했다. 완전한 오판이었다. 10초가 지나도록 테이블 조회를 하지 못했으며 중도에 중단했다.
join의 성능 관련하여 고민하게 된 계기가 되었다. 관련한 테스트를 아래와 같이 진행해봤다.
함수형 프로그래밍과 javascript es6
앞서 블로그는 es5를 기반으로 작성하였다.
es6 이후부터는 iterator, iterable, generator, spread operator 등 함수형 프로그래밍을 더 강력하게 만들어줄 기능이 추가되었다.
es5에서 구현한 함수를 es6에 맞춰 재작성 하였다.
call과 apply
모든 함수는 call과 apply 메서드를 가지고 있다.
call과 apply는 함수에 인자를 넣는 것과 거의 동일하게 동작한다.
다만 call은 인자 각각을 넣고 apply는 배열을 넣는다. fn(...arg) 와 fn(arr) 와 유사하다.
다만, call과 apply의 첫 번째 인자는, 해당 함수의 컨텍스트로 사용할 객체를 넣는다. 함수 내부에 this 를 사용할 경우 값을 삽입하며, 사용하지 않으면 null을 삽입한다.
!, !! Double Exclamation Marks
! 의 경우 부정. !undefined 와 !null 은 true로 반환한다.
!! 의 경우 부정의 부정. undefined과 null, false 일 경우 false를 반환한다.
!!은 사실상 undefined과 null, false을 한번에 처리하는 아주 편리한 기능이다.
함수형 프로그래밍 함수형 프로그래밍(函數型 프로그래밍, 영어: functional programming)은 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나이다. 명령형 프로그래밍에서는 상태를 바꾸는 것을 강조하는 것과는 달리, 함수형 프로그래밍은 함수의 응용을 강조한다. (출처 : 위키피디아) 함수형 프로그래밍은 함수의 조합을 통해 로직을 구현하는 것에 있다. 함수형 프로그래밍은 순수함수의 구현을 목표로 한다. 순수함수는 언제 어디서든 하나의 함수에 동일한 값을 넣으면 동일한 결과를 반환한다. 순수함수를 구현하기 위해서는 함수형 프로그래밍은 불변성을 유지하고 부수효과(side effect)를 제거한다. 삽입된 인자는 변경되면 안된다. 내부에서 별도의 복사한 데이터를 생성하고 결과값을 리턴한다. 인자가 처리되는 과정에서 함수 외부에 영향을 미쳐서는 안된다. 부수 효과가 없어야 한다. 인자가 처리되는 과정에서 함수...
다양한 환경에서 자바스크립트를 사용하기
자바스크립트의 경우 크롬에서 개발자 모드로 사용할 경우 테스트할 수 있다.
혹은 웹 에디터를 사용해도 좋다. 나는 다음 웹 사이트를 자주 사용한다. https://jsbin.com/?html,output
vs code를 사용하는 것도 편하다. node를 설치하고 js를 실행하는 빌드를 사용하면 된다.
gitignore가 동작하지 않는다.
인텔리제이나 기타 환경에서 gitignore를 등록하였음에도 불구하고 기능이 동작하지 않는 경우가 있다.
이때 인텔리제이의 캐시를 초기화 해도 동작하지 않을 수 있다.
이 때 문제는 깃 자체의 캐시로 인한 문제이며, 이는 다음과 같이 해소한다.
단독으로 단위 테스트를 하면 정상 동작한다. 한편, @SpringBootTest 통합 테스트 후 테스트를 통과하지 못했다.
스프링부트는 어너테이션 @SpringBootTest을 통하여 통합 테스트(integration test)와 단위테스트(unit test)를 구분한다. 전자는 어플리케이션 전체를 띄운다.
기본적으로 유닛 테스트를 권장한다. 리소스를 최소한으로 사용하고 OOP가 잘 적용 되었음을 보여주기 때문이다.
다만, 통합 테스트의 결과가 유닛 테스트에 영향을 미칠 것이라 생각하지 못했다.
단독으로 유닛테스트를 수행했을 때는 정상적으로 동작하였다.
패키지 전체 테스트를 수행할 때, 통합 테스트가 수행된 후, 유닛테스트를 할 경우 실패하였다.
(예상하건데) 통합 테스트가 수행될 경우, 통합테스트 기반으로 유닛 테스트가 수행되어 발생한 문제로 보인다.
해당 문제를 재연하였다. 해당 코드는 다음과 같다 : https://github.com/infoqoch/openstudy/tree/master/unittest-pollution
통합테스트(@SpringBootTest)에서의 메인(main)과 테스트(test)의 동시 호출 문제
현 문제는 reflection 사용 중에 발생한 문제이다.
특정 어너테이션을 읽고, 해당 어너테이션의 값이 유일함을 보장해야 한다고 가정한다. 대표적으로 스프링의 @GetMapping 가 그러하다.
이때 메인과 테스트에서 둘 다 동일한 어너테이션과 value를 구현하고 싶을 수 있다. 예를 들면 아래와 같다.
들어가며
여러번 세팅을 하지만 항상 애를 먹었던 것 중 하나가 로그이다. 로그를 하나 하나 디테일하게 설정하는 것이 어렵다.
특히 특정 로그는 발생시키고 다른 로그는 제거하는 로직을 구현하는 것 또한 힘들었다.
로깅이 원하는 방향대로 동작하지 않아 힘들었는데 ㅠ 이 글이 많은 분들께 도움이 되기를 바란다!
스프링 프로파일?
스프링 프레임워크를 동작할 때, 프로파일에 따라 설정을 달리할 수 있다
간단하게는 dev - prod 등 개발과 운영으로 나눌 수 있고, 필요에 따라 더 복잡하게 구현할 수 있다.
프로파일 설정에 대한 블로그는 매우 많다. 하지만 예전 방식도 존재하고, 아예 동작하지 않는 세팅도 존재하였다. 프로파일 설정 방법을 찾는데 생각보다 많은 시간이 들었다.
실제로 어플리케이션이 동작하는 것을 확인하고, 그것을 간단하게 정리하였다. 도움이 되기를 바랍니다!!
스프링 부트 2.7.1 버전을 사용하였다.