많은 언어에서 상호 참조는 안티 패턴으로 인식된다. 참조를 한다는 뜻은 클래스(구현체, 모듈, 기타 등등) 하나를 설명하기 위해서는 다른 클래스의 도움을 받아야 한다는 것이다.
Class A가 B를 의존하고, Class B가 A를 의존하는 경우.
Class A가 B를 의존하고, Class B가 C를 의존하고, Class C가 다시 A를 의존하는 경우.
즉 의존 관계도를 그릴 때, loop 가 이뤄진 경우를 말한다. 프로젝트가 작을 때는 쉽게 파악이 되지만, 이해 관계자들도 떠나고, 프로젝트가 커져 의존관계가 큰 원을 그리는 경우 (A -> B -> C -> D -> E -> A)는 논리적으로 발견하기 힘들어진다.
왜 안 좋은가?
패키지 내부에서의 상호의존은 경우에 따라 허용해야 할 수도 있지만 범용적인 예를 다뤄보자. 상호 의존을 하게 되면 의존을 하는 클래스들끼리 강한 결합을 하게 된다. A가 변했을 때, 죄 없는 B도 같이 변해야 할 필요성이 있다는 것이다. 강한 결합은 의도하지 않은 부수효과를 발생하고 결국 전체적인 질이 하락하게 된다. 정원에 난 잡초 하나를 뽑지 않고 놔두면, 잡초는 무럭무럭 옆으로 자라난다.
개발자는 자신이 관리하는 코드를 깨끗하고 명료하게 관리해야 할 책임이 있다.
어떻게 파훼해야 하는가?
대부분의 문제는 파인만 알고리즘으로 풀 수 있다.
문제를 쓴다.
매우 깊게 생각한다.
답을 쓴다.
Class A가 B를 참조해야 하는 이유를 골똘히 생각해보자. 왜 참조하고 있는가?
그리고 Class B가 A를 참조해야 하는 이유를 골돌히 생각해보자. 왜 참조하고 있는가?
의존성의 방향을 한쪽 방향으로 바꿔줄 필요가 있다. 의존성의 방향을 한쪽으로만 통제하면 변경에 영향을 받는 부분을 명확하게 이해할 수 있어진다.
혹은 A와 B를 모두 알고 있는 Class C를 만들어서 A와 B에서 의존적으로 하는 일을 위임받아서 하는 방법도 있다.
간혹 언어의 특성을 이용해서 문제를 잠시 회피하는 방법도 있다. (C++ 에서 포인터를 사용한다거나, 모듈을 하나씩 빌드한다거나.)
마라톤에 떠 있는 application은 framework 레벨에서 헬스체크를 지원한다.
헬스 체크는 사실 두종류 인데, mesos level 헬스체크와 marathon level 헬스체크가 있다.
디폴트는 marathon 레벨의 헬스체크인 HTTP` 다.
marathon level 헬스체크
HTTP (default)
HTTPS
TCP
COMMAND
mesos level 헬스체크
MESOS_HTTP
MESOS_HTTPS
MESOS_TCP
marathon 1.4 버전부터 HTTP, HTTPS, TCP 헬스체크가 deprecated 되었다.
동일한 기능을 제공하는 MESOS_HTTP, MESOS_HTTPS, MESOS_TCP로 갈아타면 된다.
mesos level 헬스체크의 장단점
장점
newtowking failures 에서 자유롭다
헬스체크가 작업을 실행하는 agent 에 위임되므로, 작업량을 수평적으로 확장할 수 있다.
단점
agent 에서 수행되므로 리소스가 더 소모된다.
Mesos-level health checks require tasks to listen on the container’s loopback interface in addition to whatever interface they require. If you run a service in production, you will want to make sure that the users can reach it.
Spring 5에 들어가면서 많은 변화가 있었습니다.
기본적으로 Webflux라는 비동기, 리엑티브 웹 프레임워크가 등장했고, Pivotal & Spring의 Reactive 구현체인 Reactor가 본격적으로 적용되기 시작했습니다. 게다가 Spring boot부터는 공식적으로 JSP를 지원하지 않더니, Spring 5를 사용하는 Spring boot 2부터는 기본 내장 웹서버가 톰캣에서 네티로 바뀌었습니다. 그렇기 때문에 기본적으로 서블릿도 사용하지 않죠. 그러기에 Webflux에서는 독자적으로 구현한 ServerRequest, ServerResponse 등을 사용합니다.
(물론 Spring MVC가 완전히 없어진 것은 아니기 때문에 원한다면 추가적은 설정을 통해서 이전과 같이 JSP와 톰캣을 사용할 수는 있습니다.)
Webflux에서는 기존 Spring MVC에서 사용하던 방법과 동일하게 어노테이션(@Controller, @RequestMapping 등)을 통해서 컨트롤러를 구현할 수 있지만 새로운 RouterFunction이라는 것을 이용해서 구현할 수도 있습니다. SpringOne platform의 키노트에서는 ‘XML은 똥이다’라고 말하면서 @Configuration 어노테이션과 함께 빈 설정을 코드내에서 하도록 권장하더니, 이번에는 어노테이션도 마음에 안들기 시작했는지 컨트롤러를 어노테이션을 통해 구현하는 것이 아닌 함수형 페러다임을 따라서 구현하는 방법을 내놓았습니다.
Phil Webb; Pivotal Spring Boot Lead
Sébastien Deleuze; Pivotal as a Spring Framework and Reactor commiter
아래의 코드는 Spring Webflux에서 RouterFunction을 구현하는 예제입니다.
웹서버에서 제공하는 API를 한곳에서 볼 수 있고 URL과 처리 로직간에 어노테이션이 아니라 함수를 지정함으로써 보다 명확하기는 하지만… 아직 저는 RouterFunction이 깔끔하다는 또는 가독성이 좋다는 말은 들어보지 못한 것 같습니다. 저도 그다지 깔끔하다는 생각이 들지는 않네요. 그런데 Spring 5에는 저 코드를 깔끔하고 보기 좋게 작성할 수 있는 방법을 제공합니다. 사실 위 Sébastien Deleuze의 PPT에도 힌트가 있습니다. 바로 개발에 Java가 아닌 Kotlin을 이용하는 것이지요!
Spring 5 with Kotlin
최근 여러 유명 프레임워크에서 Kotlin을 정식으로 지원하겠다는 소식이 들려왔습니다.
대표적으로 Spring Framework는 5 버전부터 Kotlin을 정식으로 지원하며, Spark, Vert.x도 Kotlin을 지원하기 시작했습니다.
여기에서는 그중 Spring Framework를 중심으로 Kotlin를 사용해 확장된 기능에 대해서 알아보고자 합니다.
spring 5에는 0.4%나 Kotlin으로 작성되었다. 이는 Spring에서 무려 2번째로 많이 사용된 언어라는 의미한다!
Kotlin을 위해 구성된 Spring 프로젝트 템플릿은 SPRING INITIALIZR에서 손쉽게 구성하여 받을 수 있다.
Spring Webflux
RouterFunction
물론 특별히 프레임워크나 라이브러리에서 Kotlin을 지원하지 않더라도 Java와 호환성이 좋기 때문에 별로 큰 문제없이 Kotlin을 사용할 수는 있습니다. Spring 5에서 일부 모듈이 Kotlin으로 개발되었다고는 하지만 해당 모듈들을 살펴보면 실제 핵심 로직을 구현한 것은 아닙니다. Kotlin으로 작성된 부분은 주로 기존 기능을 확장하여 Java로는 불가능하거나 매우 장황해지는 부분을 보다 편하고 깔끔한 코드로 작성할 수 있도록 돕는 역할을 하는 모듈이 대부분입니다. Java로는 표현하는데 한계가 있던 부분들을 Kotlin으로 개선한 것이죠.
이 확장 모듈 중에는 Webflux에서 사용할 수 있는 모듈이 있으며 그중 가장 대표적인 것이 RouterFunctionDSL입니다. RouterFunctionDSL을 사용하면 위에서 잠깐 살펴봤던 RouterFunction을 보다 깔끔하게 작성할 수 있습니다. 위에서 Java로 작성한 RouterFunction을 Kotlin으로 재작성해보겠습니다.
Kotlin의 확장 기능을 통해서 보다 보기 편하게 RouterFunction이 개선되었습니다! 코드를 장황하게 만들던 의미없는 코드들이 많이 줄어들고 정말 API를 작성하는데 필요한 부분만 남아서 한눈에 알아보기에도 좋네요. 개인적으로 기존에 사용하던 어노테이션을 통해서 컨트롤러를 구현하는 것보다 이 코드가 더 깔끔한 것 같습니다.
Model
RouterFunction을 사용하지 않고 기존의 어노테이션을 사용하더라도 작은 편한점이 하나 있습니다. 깨알 같은 변화이지만 가독성은 더 좋아지는 것 같네요.
Spring에서 제공하는 웹 클라이언트 클래스인 RestTemplate을 사용한다면 응답 값의 데이터 타입이 조금이라도 복잡해지면 코드가 장황해집니다. getForObject 같은 간단한 메소드를 사용할 수 없고 아래의 코드와 같이 같은 exchange와 함께 ParameterizedTypeReference<...>() {}라는 정말 이름이 길어서 손가락을 아프게하는 클래스를 사용해야할 때가 있습니다. 이 역시 Kotlin으로 확장되어 손가락을 덜 아프게 해줍니다.
참고로 코틀린의 특성 덕분에 테스트를 수행하는 메소드의 이름을 보다 자유롭게 작성할 수 있습니다. 물론 무엇을 테스트하는 코드인지 주석을 달아주는 방법도 좋지만 테스트 메소드들을 한 곳에 모아서 볼때(예를 들면, mvn test 또는 Intellij에서 전체 테스트 코드를 실행했을 때) 메소드명만 봐도 어떤 역할을 하는지 쉽게 알 수 있을 것입니다.
이외에도 다른 Kotlin이 확장 모듈이 존재하지만 여기서는 이런 것도 있구나하고 간단히 소개하는 자리이기 때문에 전부 구구절절 설명하지는 않을 것입이다(사실, 아직 많지는 않아서 그냥 구글에서 몇 개 찾아보는 것이 더 효율적일 것입니다). 몇몇 Kotlin 확장 모듈 및 Kotlin이 가진 이점 덕분에 Spring 5에서는 보다 가독성이 좋은 깔끔한 코드를 작성할 수 있으며, Kotlin을 통해 보다 유연한 클래스 및 함수를 설계할 수 있을 것입니다. 또한, Kotlin의 가장 큰 장점 중 하나인 Null safety를 통해서 보다 견고한 애플리케이션을 개발하고, 불필요한 코드들을 제거한 간결한 코드 덕에 생산성도 향상될 것 입니다. 정말 깔끔하네요.
등록을 마치고 커피를 한잔 뽑아서 ‘Lean Coffee’를 하러 커피 마시는 공간으로 총총.
테이블마다 큰 주제가 있고, ‘특정한 이야기 주제가 없는 구조화된 대화 방법’이라고 한다. 가볍게 네트워킹. 내가 참여했던 테이블은 ‘Communication & imporv’였다. TDD 머시기 테이블에 잠깐 갔었는데, 가벼운 이야기를 주로 나누고 있어서, 차라리 커뮤니케이션에 대한 이야기를 하는 게 좋다고 판단했다.
직장동료들과의 관계 이야기, improv 이야기, 에자일에 대한 경험들을 이야기하다 보니 시간이 훌쩍 지났다. imporv라는 것을 새로이 알게 된 것도 엄청난 수확. 원래는 조승빈 아저씨의 ‘애자일, 한때의 유행인가’와 정우진 아저씨의 ‘DevOps’이야기를 들으러 갈 계획이었으나…. ‘개발자를 위한 imporv’를 들으러 가게 되었다.
엄청 잘한 선택! (강연은 동영상으로 다시 보면 되니까…)
생각보다 빠르게 커피타임이 지나고, 동료와 합류하여 첫 번째 세션인 ‘state of the art in agile’을 들으러 갔다.
소트웤스의 CTO인 마이크 아저씨의 강연
소프트웨어를 바라보는 관점에 대해 유명한 아저씨로부터 도덕책 같은 이야기를 많이 들었다. 새겨들을 것은 많지만 큰 감동은 없는.. 짧게나마 요약하면 이렇다.
CI, CD 쓰세요. 꼭.
tdd 하세요.
microservice 짱 좋아요.
microservice들로 api eco 시스템을 만들어요.
products, not projects 프로젝트에 치고 빠지듯이 프로덕트를 만들면 안 되고, 장기적인 관점에서 프로덕트를 바라봐야 한다.
애자일은 혼자 하는 게 아니라, 같이 만들어가는 거라는 걸 위의 맥락에서도 많이 느꼈다.
2. 애자일 전파를 위한 혼자만의 싸움 전략
두 번째 세션은 SKP에서 애자일 전파를 위해 고군분투하는 신원 님의 세션.
본인이 회사에서 애자일을 전파하기 위해서 겪었던 경험들을 나눠주셨다.
‘애자일’이라는 키워드를 받아들이는 사람들의 뉘앙스, 분위기, 태도, 이해도… 들이 모두 다르고 이 모든 사람들을 한 번에 관통하는 은탄환은 존재하지 않음을 반증하고 계셨다.
5가지 싸움 전략과 함께, 마무리는 ‘애자일 코치 좀 더 뽑아주세요.’
짧은 경험에 비춰보면, 성공적인 애자일 사례에는 ‘좋은 애자일 코치가 있었다.’라는 말을 많이 듣게 된다. 두 번째로 많이 들리는 이야기는 상호 소통이 원활하고 팀 전체의 가치 공유가 명확하게 되어있어서, Goal 설정이 잘된 경우에 ‘이번에 우리 애자일 좀 된 것 같아.’ 라는 말이 나왔다고 한다.
여러 사람이 한마음 되는 것이 얼마나 어려운가…
여기까지 듣고 다시 밥 먹으러 총총. 아침부터 너무 배고팠던지라 빠르게 뚝딱.
3. Design for growth, A tactical toolkit for middle management: the leader of knowledge workers
트위터에 Enterprise Agile coach를 하시는 Luk lau 아저씨의 세션.
중간 관리자, 혹은 어느 정도 규모 있는 조직을 세팅할 때 많은 교훈을 줄만한 세션이었다.
NBT의 조직 히스토리와 함께, 소프트웨어 아키텍트로써 어떤 일들을 처리하고 있는지를 나누는 세션이었다. 드로이드 나이츠에서 뵀었는데, 반가웠다.
6. 개발자를 위한 Imporv
즉흥연기 임프로그 imfrog 에서 준비해주셨다. (애자일 콘퍼런스 후기의 주인공)
애자일은 ‘소통’이다.
상호 ‘소통’ 증진을 위한 imporv 세션. ‘신뢰’, ‘공감’, ‘팀빌딩’, ‘에너지’, ‘리더십’, ‘yes and’.
6가지 주제 리더들과 20분간 짧고 굵게 교감 + 회고. 하는 세션이었다. 아쉽게도 시간이 충분하지 못하여, ‘에너지’, ‘리더십’ 파티에는 가보지 못했다.
(신뢰, 공감, 팀빌딩에서 행했던 활동들의 순서가 안 맞을 수 있다… 정정해주시면 감사합니다 :D )
‘신뢰’, ‘공감’, ‘팀빌딩’
상호 간의 신뢰를 구성하기 위해서 아이컨택을 하고 마임(행동?)을 진행했다. 액션을 진행하면서 서로 눈을 먼저 바라봐야 했다. ‘집 잽 잡’, ‘공던지기’, ‘연상 키워드 말하기’. 워밍업이 좀 끝나고 이제 슬슬 본 게임(시작하기 전에 탈출을 못하도록 출구가 봉쇄되었다.)
‘어깨동무를 둘이 한 몸인 것처럼 이야기하기’, 주거니 받거니 하면서 한 명인 것처럼 말을 이어나갔는데, 주거니 받거니가 진짜 어려웠다. 차라리 따라가는 건 쉬웠는데, 한 문장 내에서 주-부 를 전환하는 건.. 어렵다. 처음 하는데 쉬운 게 있을까.
‘내 앞사람이 한 말에 대한 감정을 다른 대화로 이어나가기’, ‘마임에 대한 상황을 말로 표현하기’, 내 앞사람의 행동, 말에 대해 매우 집중하고, 감정을 동조하거나 이어나가는 활동이었다. 짧은 시간 동안 타인에 대한 높은 집중력을 요구하는 활동. 사회생활하면서 이만큼 집중력을 발휘한 적이 몇 번이나 있었는가?
‘yes and’
그리고 대망의 ‘yes and’ 상대방의 말에 ‘맞아~ 그리고~’를 하고 대화를 이어가는 활동. 아무 말 대잔치지만 내 앞사람과의 유대감이 어마어마 해졌다. 나와 yes and 했던 두 분의 얼굴이 아직도 기억이 난다. ‘나와 함께하는 이 사람들은 안전하다’라는 유대감이 아주 깊이 각인되었다. 나중에 다시 만나도 엄청 친하게 인사할 것 같은..
생판 남인 사람들과 모여 ‘미친 짓도 함께 하니 재미있다.’를 체감했다. 안전한 곳을 설정하고, 안전한 곳에서 유대감을 생성하고, 사회적 가면을 벗어둔 채로 상호 유대를 격하게 함양.
콘퍼런스가 끝나고 이 사람들과의 유대감을 지속해보고자 얼굴책에도 다시 가입.
다소 비쌌지만(사장님 감사해요),
얻은 게 정말 많은 콘퍼런스였다. 고생하신 분들께 너무 감사드리고, 아쉬운점은 다음 콘퍼런스에서 해우하시기를!!
행사 앨범에서, https://www.facebook.com/AgileKoreaConference/posts/1137604536373266
이전 문서에서는 Spring Boot에 새롭게 추가된 spring-boot-test-starter에 포함되어있는 모듈들과 기본적인 Spring Boot 테스트 모듈에 대해서 알아보았습니다. 이번에는 좀 더 세부적으로 들어가서 JSON 테스트, WebMvc 테스트, JPA 테스트, JDBC 테스트, Mongo 테스트, RestClient 테스트에 대해서 알아볼 것 입니다.
@JsonTest
@JsonTest 어노테이션을 사용하면 보다 편하게 JSON serialization과 deserialization을 테스트해볼 수 있습니다. @JsonTest 어노테이션은 ObjectMapper와 @JsonComponent 빈을 포함한 Jackson의 테스트를 위한 모듈들을 자동으로 설정합니다.
테스트를 위한 빈으로 JacksonTester, GsonTester, BasicJsonTester 등이 있습니다. 이를 주입받아서 사용하면 보다 편리하게 JSON을 테스트해볼 수 있습니다. 그리고 Assertj는 JSON을 위한 기능들을 제공합니다(JSONassert, JsonPath를 기반으로한). 아래는 JSON serialize와 deserialize를 테스트하는 예제입니다.
이전 문서에서 client-side에서 API를 테스트하는 TestRestTemplate을 살펴봤다면, 이번에는 server-side에서 API를 테스트하는 @WebMvcTest 어노테이션에 대해서 알아볼 것 입니다. 해당 어노테이션은 기존에 spring-test에서 컨트롤러를 테스트할 때 많이 사용하던 MockMvc에 관한 설정을 자동으로 수행해주는 어노테이션입니다. @WebMvcTest 어노테이션을 사용하면 테스트에 사용할 @Controller 클래스와 @ControllerAdvice, @JsonComponent, @Filter, WebMvcConfigurer, HandlerMethodArgumentResolver 등을 스캔합니다. 그리고 MockMvc를 자동으로 설정하여 빈으로 등록합니다.
위 코드처럼 MockMvc로 요청을 한 뒤 MvcResult로 받어서 asyncDispatch로 감싸줄 필요가 있습니다.
@DataJpaTest
Spring Data JPA를 테스트하고자 한다면 @DataJpaTest 기능을 사용해볼 수 있습니다. 이 어노테이션과 함께 테스트를 수행하면 기본적으로 in-memory embedded database를 생성하고 @Entity 클래스를 스캔합니다. 일반적인 다른 컴포넌트들은 스캔하지 않습니다.
참고로 @DataJpaTest는 @Transactional 어노테이션을 포함하고 있습니다. 그래서 테스트가 완료되면 자동으로 롤백하을 하기 위해서 직접 @Transactional 어노테이션을 달아줄 필요가 없습니다. 그런데 만약 @Transactional 기능이 필요하지 않다면 아래와 같이 줄 수 있습니다.
@DataJpaTest 기능을 사용하면 @Entity를 스캔하고 repository를 설정하는 것 이외에도 테스트를 위한 TestEntityManager라는 빈이 생성됩니다. 이 빈을 사용해서 테스트에 이용한 데이터를 정의할 수 있습니다. 아래는 @DataJpaTest를 사용하여 테스트를 수행하는 예제입니다.
Spring Data JPA를 사용하지 않더라도 데이터베이스 테스트를 해볼 수 있습니다. @JdbcTest는 @DataJpaTest와 비슷한 설정을 수행하지만 순수 JDBC 테스트를 준비합니다. @JdbcTest 어노테이션을 사용하면 마찬가지로 im-memory embedded database가 설정되며, 테스트를 위한 JdbcTemplate이 생성됩니다.
@DataMongoTest
최근 점점 많은 인기를 얻고 잇는 NoSQL DB인 MongoDB에 대해서도 편리한 테스트 기능을 제공합니다. @DataMongoTest 어노테이션이 이를 위한 기능을 제공하며 설정하는 내용은 @DatajpaTest와 유사합니다. 위에 다른 데이터 테스트 모듈과 유사하게 im-memory embedded MongoDB를 사용하지만, @DataMongoTest는 @Entity가 아닌 @Document를 스캔하며 MongoTemplate을 생성합니다.
@RestClientTest 기능은 자신이 서버 입장이 아니라 클라이언트 입장이 되는 코드를 테스트할때 유용합니다. 예를 들면, Apache HttpClient나 Spring의 RestTemplate을 사용하여 외부 서버에 웹 요청을 보내는 경우가 있습니다. @RestClientTest는 요청에 반응하는 가상의 Mock 서버를 만든다고 생각하면 됩니다. 내부 코드에서 웹 요청이 발생할 경우 @RestClientTest로 인해서 생성된 가상의 서버가 응답을 해줍니다. 물론 그 가상의 서버가 어떤식으로 응답을 할지 정의할 수 있습니다. 이를 사용하면 보다 RestTemplate 같은 객체를 Mock 객체로 바꿔서 테스트하는 것보다 리얼 환경에 가깝게 단위 테스트를 수행할 수 있습니다. 이 기능을 사용하면 자동으로 MockRestServiceServer라는 빈이 생성되며 이를 이용하면 손쉽게 요청과 응답에 대한 설정을 할 수 있습니다.