프로그래밍/Book

자바 개발자를 위한 97가지 제안

가라멜 2021. 1. 12. 23:13
반응형

우연히 인터넷 서점을 둘러보다 발견한 책.

제목이 맘에 들어서 구매했다.

역시나 후회 하지 않을 만한 내용들로 가득하다.

 

특히 나 스스로 부끄러울만한 내용이 많았는데,

 

테스트(TDD)의 중요성과 JVM, 자바의 조금 더 본질적인 면을 주로 강조했다는 점에서 

현재 나의 상태를 되돌아 보는 계기가 되었다.

업무와 프로젝트 일정이 바쁘다는 이유로 테스트를 등한시 하다보니

테스트도 정말 간단한거 몇개만 해놓고 그것마저 수행하지 않는 경우가 많았는데..

이제라도 열심히 해보자.

 

JVM 이나 GC도 조금더 파봐야 겠다.

짧지 않은 시간동안 java 를 사용했는데 너무 대충 사용한것 같다.

 

 

 

 

 

조금 더 상세한 정리 는 요 링크

 


자바 개발자를 위한 97가지 제안

자바 개발자를 위한 97가지 제안(케블린 헤니, 트리샤 지/ 제이펍/ 2020)

알아야 할 것도, 해야 할 것도, 그 일을 수행하는 방법도 너무나 많기에 어느 한 사람이나 하나의 논리도 '하나의 정답'을 주장할 수는 없다.

각기 다른 저자의 이야기는 반드시 일맥상통하지 않으며, 그럴 의도도 없다. 오히려 그 반대다. 저마다의 이야기는 그 독창성에 가치가 있다.
이 모음집의 가치는 각각의 저자가 자기 생각을 보완하고 확인하며 때로는 다른 저자의 생각에 반박하는 것에 있다.
누군가의 생각이 특별히 더 중요한 것도 아니다. 읽어본 내용에 반응하고, 이를 반영하며 서로 연관짓고, 자기 생각과 지식,
경험에 기반해 어느 것에 더 무게를 둘 것인지 오로지 독자 여러분에게 달렸다.

01 자바만으로도 충분하다

자바는 훌륭한 언어이며 자바 클래스 라이브러리는 범용으로 설계되었다. 파일을 다뤄야 한다면 java.nio 라이브러리를 쓰면 된다. 데이터 베이스는 java.sql 라이브러리가 맡아준다.
범용적이며 재사용 할 수 있다는 관점에서 보면 일종의 룰 엔진같은 것을 도입하고 싶어지기도 한다. 하지만 결국 룰 엔진을 설정하는 것도 프로그래밍이며, 이 경우 대부분은 자바보다 못한 언어를 사용하게 된다. 왜 그냥 자바로 코드를 쓰지 않을까? 자바로 작성한 코드는 읽기 쉽고, 심지어 자바 프로그래머가 아니어도 쉽게 유지보수할 수 잇는 코드를 산출할 수 있다.
유행을 좇지 말자

02 확인 테스트

assertEquals("", functionCall())

위의 코드처럼 빈 값이나 의미 없는 값을 확인하는 테스트 코드를 작성해 본 적이 이는가?
보통 functionCall 함수가 문자열을 리턴하는데 이 문자열이 정확히 어떤 값이어야 되는지 모르지만 리턴값을 보면 맞는지 아닌지 알 수 이는 경우 이런 코드를 작성한다.
물론 처음 테스트를 실행하면 실패하게 되는데, 그 다음 리턴값을 복사해서 assertEquals 함수 파라미터로 복사해 넣는다. 이제 테스트를 다시 실행해보면 통과할 것이다.
이걸로 끝! 필자는 이 방법을 확인테스트(approval testing) 라고 부른다.

확인테스트는 어떤 경우에 활용할 수 있을까?

  • 변경해야 할 단위 테스트가 없는 코드
  • JSON이나 XML을 리턴하는 REST API와 함수
  • 복합 객체를 리턴하는 비즈니스 로직

03 AsciiDoc 으로 자바독 확장하기

간혹 자바독이 제공하는 API 문서, 즉 패키지와 프로젝트 개요 페이지 이외의 것이 필요한데, 이러한 것들은 자바독만으로 충분히 문서화할 수 없다.
아스키독이 이러한 것의 대안으로, 마크다운 같은 경량 마크업 형식처럼 즉각 읽을 수 잇는 구조로 되어 있다.

04 컨테이너를 제대로 이해하자

레거시 자바 애플리케이션을 레거시 자바 가상머신(JVM)상에서 있는 그대로 컨테이너화 하는 것은 위험하다. 오래된 JVM 을 도커 컨테이너에서 실행하면 어처구니 없는 일이 일어나기 때문이다.

컨테이너는 사실상 런타임 패키징 매커니즘의 표준이 되어가고 있다. 적정 수준의 격리화, 향상된 자원활용, 여러 호나경으로 애플리케이션을 배포할 수 있는 기능 등 여러 장점을 제공하기 때문이다.
자바의 경우 레거시 자바 애플리케이션과 의존성 그리고 애플리케이션이 사용하는 오래된 버전의 JVM 까지도 컨테이너에 욱여넣는 경우가 많다.
레거시 자바 애플리케이션 및 관련 환경을 컨테이너화하면 오래된 애플리케이션도 철 지난 인프라스트럭처에서 벗어나 최신 인프라스트럭처에서 실행할 수 있다. 하지만 이 방법의 장점을 취하려다 보면
JVM 어거노믹스(ergonomics)으로 인한 여러 위험 요소와 맞닥뜨리게 된다.
(JVM 어거노믹스 : JVM과 가비지 컬렉션이 애플리케이션 성능을 향상하기 위한 목적으로 실행하는 자기 학습 프로세스)
낮은 버전의 JVM 은 자신이 컨테이너안에서 실행중이라는 점을 인지하지 못해서 컨테이너가 아닌 호스트 OS의 지표를 측정하려 한다. 따라서 JVM이 잘못된 지표를 사용해 스스로를 튜닝하려고 시도하는 경우도 있다.

다음 명령 실행 시 JVM 어거노믹스가 설정한 JVM 매개변수를 확인 할 수 있다.
java -XX:+PrintFlagsFinal -version |grep ergonomic
JVM은 기본적으로 컨테이너 지원이 활성화되어 있지만
-XX:=UseContainerSupport
JVM 플래그를 이용해 비활성화 할 수 있다.

레거시 JVM을 도커 컨테이너에서 실행하는 것은 권장할 만한 것은 아니다. 하지만 컨테이너화가 유일한 옵션이라면 적어도 레거시 JVM 이 컨테이너에 할당된 자원을 초과해서 사용하지 않도록 하자.
가장 이상적이면서도 명확한 해결책은 기본적으로 컨테이너를 인지할 뿐 아니라 최신 버전이면서 안전한 런타임을 제공하는 버전의 JVM을 사용하는 것이다.

05 행위를 구현하는 것은 쉽지만 상태를 관리하는 것은 어렵다

캡슐화를 활용하면 소프트웨어 개발 분야에서 쉽게 찾아볼 수 있는 상태와 복잡도의 증가에 순응할 수 있다.
상태를 내면화해서 다른 컴포넌트로부터 숨기며, 안전하게 디자인된 API로만 상태를 변경할 수 있게 하는 것이 캡슐화의 기본 개념이자 복잡한 정보 시스템을 디자인하고 구현하는 핵심이다.
최소한 자바 세계에서는 제대로 캡슐화된 시스템 구축에 대한 몇가지 권장 사례가 제대로 전파되지 않고 있다. 별다른 기능을 수행하지 않는 클래스의 자바빈 속성은 그저 게터와 세터에 의해 내부 상태를 외부로 노출하는 것이 일반적이며, 이미 대중화된 자바 엔터프라이즈 아키텍처는 거의 몯느 비즈니스 로직을 서비스 클래스에 구현하도록 하고 있다.
행위에 의해 발생한 버그를 특정 짓는 것은 비교적 '쉬운' 편이다. 반면 코드는 제대로 동작하는 것 같은데 여전히 버그가 존재하는 경우는 훨씬 복잡하다. 필자의 경험상 가장 해결하기 어려운 버그는 모순된 상태로 인해 발생하는 버그이다.
클래스가 제공하는 인터페이스 자체가 너무 가변적이고 접근이 쉬워서 어디에서든 아무런 방어 장치 없이 상태를 변경시킬 수 있다.

그렇다면 이 문제를 어떻게 해결할 수 있을까? 불변성(immutability) 이 그 해법 중 하나이다.
객체가 불변임을 보장할 수 있고 객체를 생성하는 시점에 상태의 무결성을 검사할 수 있다면 시스템은 절대 모순된 상태가 되지 않을 것이다.
올바르게 구현한 팩토리 메서드와 빌더 패턴을 이용하면 가변 상태를 최소화 할 수 있다.
그러므로 세터를 자동으로 생성하지 말자. 대신 세터에 대해 오래 생각해 보자. 만일 요구사항 때문에 세터를 추가해야 한다면 세터를 사용한 후 내부 상태를 보호하고 검증하기 위한변질방지계층 을 사용하는 것을 고려하자.

06 JMH로 조금 더 쉽게 벤치마킹해 보자

JVM에서의 벤치마킹, 특히 마이크로벤치마킹은 어렵다.
JMH는 올바른 마이크로벤치마크를 작성하기 위한 도구다. 같은 환경에서 실행하면 결과도 비교할 수 있으므로 벤치마크 결과를 해석하는 주된 방법으로 사용해야 한다. 게다가 안정적이며 반복적인 결과를 제공하므로 프로파일링 목적으로도 사용 할 수 있다.

07 아키텍처의 품질을 체계화하고 검증하는 방법의 장점

모두가 합의한 애플리케이션의아키텍처 품질을 코드화하고 강제하는 가장 중요한 곳은 여러분의 지속적 전달 빌드 파이프라인이다. 하지만 품질 검증을 자동화했다고 해서 팀이 표준과 품질 수준에 대한 논의를 중단해서는 안되며, 팀 내 또는 팀 간 의사소통이 줄어들어서도 안 된다. 즉, 빌드 파이프라인 내에서 품질 지표를 확인하고 게시하면, 다른 방법으로는 알아차리기 어려울 수 있는 아키텍처 품질의 점진적인 저하를 방지할 수 있다.

08 문제와 업무를 더 작은 단위로 나누기

직장에서의 업무와 시스템은 크고, 큰 문제는 해결하는데 오래 거린다. 더 안좋은 점은 뇌에 기억해야 할 것이 너무 많다는 점이다.
이 문제를 해결하는 좋은 방법은 문제를 더 작은 조각으로 나누는 것이다. 더 작게 나눌수록 좋다. 일단 작은 문제를 하나 해결하면 더는 그 문제를 고민할 필요 없이 다른 문제로 넘어가면 된다. 문제를 잘 분할하면 그다음에는 작게 나눈 문제를 확인할 자동화된 테스트를 작성하게 된다. 또한 커밋도 더 자주 하게 된다. 커밋을 자주 하면 뭔가 원하는대로 동작하지 않을 때 롤백할 수 있는 지점이 생긴다

09 다양성을 인정하는 팀 만들기

협업은 이제 '훌륭한' 전문가를 구분하는 가치 있는 자질 중 하나가 됐다. 과거에는 독립적이며 자기 주도적이면 충분히 '훌륭하다'고 할 수 있었다. 현대의 우리는 모두가 피트 크루, 즉 팀의 구성원이다. 이제 문제는 다양성을 인정하면서도 성공적으로 기능하는 팀을 만드는 방법이다.
협업의 핵심은 팀 내 심리적 안정성과 신뢰를 쌓는 것이다.

10 빌드는 느려서도 안 되고 불안정해서도 안 된다

어떤 빌드 도구를 사용하든, 개발자 생산성을 책임지는 사람은 빌드 성능을 효율적으로 측정하고 성능이 떨어지는 원인을 추적하며 로컬 및 CI 빌드의 회귀 테스트를 실행한다. 이 역할을 하는 사람은 빌드 결과를 분석해 빌드 과정에서 병목을 일으키는 원인을 찾는다. 그리고 뭔가 잘못되면 보고서를 팀원과 공유하고 실패한 빌드와 성공한 빌드를 비교해 설령 자신의 머신에서 해당 이슈를 재현하지 못하더라도 정확한 원인을 찾아 낸다. 또한 개발자 생산성 담당자는 이렇게 축적된 데이터를 기반으로 빌드 프로세스를 최적화하고 개발자의 부담을 줄여준다. 사실 이 업무는 끝이 없으므로 개발자 생산성을 유지하는 일은 계속 반복된다.

11 아니, 내 머신에서는 잘 실행됐다니까!

코드로서의 인프라스트럭처 개념에 따르면, 도구를 제공하는 사람이나 조직은 사람의 개입 없이 표준화된 버전의 빌드 도구 런타임을 프로비저닝 할 수 잇는 솔루션인 래퍼를 제공한다. 래퍼는 런타임을 다운로드하고 설치하기 위한 과정을 감싼 것이다. '내 머신에서는 잘 실행됐다니까!' 라고 외치던 시절은 지났다. 한 번에 표준화하고 어디서든 실행하자. 모든 JVM 프로젝트에 래퍼 개념을 도입해서 빌드의 재현성과 유지보수성을 확보하자.

12 비대한 JAR은 이제 그만

애플리케이션을 하나의 거대한 JAR 파일을 사용하지 않고 다른 형태로 패키징해서 실행하는 것은 현대의 자바 웹 개발 환경에서는 금기시되고 있다. 하지만 프로젝트를 빌드하고 그 결과 파일을 배포하는 방법에도 단점은 있다. 그 중 한 가지 확실한 문제는 주로 JAR 파일의 크기가 스토리지 공간과 네트워크 대역폭보다 커지는 것이다. 게다가 일체형 빌드 절차는 시간도 오래 걸릴뿐더러, 개발자가 빌드를 기다리는 동안 다른 업무로 콘텍스트 스위칭하게 되기도 한다. 자바 애플리케이션을 하나의 커다란 JAR 파일에 담아 배포하는 방법은 마이크로서비스아키텍처, 데브옵스 그리고 클라우드나 컨테이너, 오케스트레이션 플랫폼 같은 기법의 등장과 더불어 점점 더 보편화 되고 있다. 하지만 일부 조직은 이런 방법에서 벗어나 '날씬한 JAR' 파일을 만들기 시작했다.

13 코드 복원전문가

우리가 100년전에 만들어진 물건을 복원하는 사람을 위해 일하고 있다는 점을 명심해. 이 사람이야말고 우리가 닮고 싶은 사람이야
최고의 코드는 나중에 그 코드를 보게 될 프로그래머를 생각하며 작성한 코드다. 그렇기에 '(모두가 바라지만 거의 항상 실패하는) 같은 것을 더 나은 방법으로 다시 만드는'일이 아니라 기존 코드를 천천히 다듬어 다시 관리할 수 잇는 상태로 재창조하는 코드 복원 전문가가 필요하다. 여기에 테스트를 조금 더 추가하고, 말도 안되는 클래스를 잘게 나누고, 사용하지 않는 기능은 과감히 제거해서 더 나아진 코드를 다시 내놓는 그런 사람이 필요하다.

14 JVM의 동시성

애초에 JVM에서 가능한 동시성 모델은 원시 스레드 뿐이었으며 여전히 자바에서 병렬과 동시성 프로그램을 작성하기 위한 기본 옵션이다.
요즘은 병렬화의 가용성과 기대로 인해 명시적 멀티스레딩의 한계가 더 명확해졌다. 스레드와 록은 너무 저수준이다. 즉, 제대로 사용하기가 어렵다. 자바 메모리 모델을 이해하는 것은 그보다 더 어렵다. 공유할 수 잇는 가변 상태를 이용해 통신하는 스레드는 대규모 병렬성에는 어울리지 않으며 메모리 접근이 제대로 동기화되지 않으면 어떤 결과가 나올 지 알 수 없다. 게다가 록을 제대로 사용하더라도 록의 목적은 병렬로 실행 중인 스레드로 제한되므로 애플리케이션의 병렬성을 오히려 감소시킨다.
공유메모리 제한을 극복하는 가장 간단한 방법은 록 대신 분산 큐를 이용해 스레드를 조율하는 것이다. 즉, 공유 메모리 대신 메시지를 전달하는 방법이며 디커플링도 향상된다. 큐는 양방향 통신에 적합하지만 지연 응답이 발생할 수 있다.

15 CountDownLatch, 친구인가 적인가?

16 선언적 표현식은 병렬성으로 가는 지름길이다

자바는 태생적으로 명령형이자 객체 기반 프로그래밍 언어다. 하지만 발전을 거듭해 왔으며 단계마다 더 많은 선언적 표현식을 도입해왔다. 선언적 표현식의 핵심에는 고차 함수가 있다. 고차 함수란 함수를 매개변수로 받으며 함수를 리턴하는 함수다.

17 더 나은 소프트웨어를 더 빨리 전달하기

  1. 전달이란 코드를 작성하고 디버깅하는 것 이상의 책임을 갖는 것을 의미한다.
  2. 더 나은 소프트웨어란 '올바른 기능을 구현'하는 것과 '올바르게 기능을 구현' 하는 두 가지 개념을 짧게 표현하는 것이다.
  3. 빨리는 전달과 더 나은 소프트웨어를 모두 지칭하며 자칫 달성이 어려운 부분이 될 수 있다.

18 지금 몇 시예요?

19 기본 도구의 사용에 충실하자

모든 자바 프로그래머에게 필요한 기본 도구 하나를 꼽으라면? 답은 javac이다. IDE에만 의존하면 IDE가 의도적으로 정보를 은닉하므로 프로그래머가 자신이 사용하는 도구를 완벽하게 습득할 수 없다. 설정은 일단 한 번 적용하면 잊게 마련이다.

20 변수를 바꾸지 말자

값이 변하지 않은 변수를 불변 요소로 정의하면서 코드의 디자인을 조금 더 세심하게 고민하게 되고 잠재적인 버그도 찾을 수 있음을 깨닫는다. 덕분에 변숫갑싱 변경되는 부분과 그 동작의 범위를 지역화해야 할 부분을 명확하게 표현할 수 있다.

21 SQL식 사고 도입하기

SQL 쿼리의 장점을 생각해보자.

  1. 조인 결과를 저장하기 위해 새 테이블이 필요하지 않다.
  2. 쿼리는 선언적이다.
  3. 도메인 전용 언어(DSL, Domain-Specific Language)는 문제에 잘 부합한다.

22 자바 컴포넌트 간의 이벤트

자바 객체지향성의 핵심 개념은 모든 클래스를 컴포넌트로 간주할 수 있다는 점이다. 자바의 이벤트는 컴포넌트의 상태를 변경하는 행위다.

23 피드백 루프

  • 개발자로서 나는 어떤 코드를 개발해야 할지 모르므로 제품 관리자로부터 요구 사항을 전달받는다. 이 과정에서 나도 요구사항을 잘못 이해할 수 있다.
  • 구현 방법을 고민하는 동안 실수할 수 있으므로 동료와 함께 짝을 이뤄 일한다. 내 동료가 나의 실수를 고쳐준다.
  • 내 동료도 사람이고 실수할 수 있으므로 단위 테스트를 작성한다. 단위 테스트가 우리의 실수를 고쳐준다.
  • 코딩 업무를 함께하는 팀이 있으므로 팀의 코드에 우리가 작성한 코드를 통합한다. 우리가 실수를 저지르면 이 코드는 컴파일되지 않는다.
  • 팀도 실수할 수 있으므로 전체 시스템을 점검하기 위한 수용테스트를 작성한다. 우리가 실수를 저지르면 수용 테스트가 실패한다.
  • 시장 상황은 계속 변화하므로 그전까지는 문제가 없던 부분도 결국에는 문제가 된다.
  • 뭔가 잘못된다는 것은 결국 비용이 드는 것이므로 지금까지 나열한 것을 최대한 자주 수행한다. 그렇게 해서 실수를 최소화한다.
  • 제대로 만드는 것을 고민하지 말고 잘못된 것을 어떻게 알아낼지, 잘못된 것을 찾았을 때 얼마나 쉽게 수정할 수 있는지를 고민하자. 왜냐하면 분명 뭔가 잘못될 것이기 때문이다.

뭔가를 잘못 이해했더라도 괜찮다

24 불꽃 그래프를 이용한 성능 확인

불꽃 그래프는 트레이스를 각 스택 수준으로 정렬해 집계한 것으로 각 스택 수준별 카운트는 코드의 각 부분의 총 실행 시간의 백분율을 의미한다. 이 백분율을 너비로 하는 블록으로 렌더링하고 각 블록을 쌓아 보면 매우 유용한 결과를 볼 수 있다.

25 지루하더라도 표준을 따르자.

자바 초창기에는 시장에 호환성이 없는 애플리케이션 서버가 많았으며 서버 벤더는 완전히 다른 패러다임을 따르고 있었다. 따라서 여러 서버를 모두 이해하는 것은 어려웠고 애플리케이션을 한 서버에서 다른 서버로 이전하는 것은 불가능에 가까웠다.
JDBC, JNDI, JPA 같은 API가 등장하며 이미 구현된 제품의 추상화, 간결화, 통합이 이루어졌고 J2EE의 등장으로 J2EE API 만 알면 애플리케이션을 개발하고 배포할 수 있었다.
현재 웹/자바스크립트 생태계도 유사한 상황에 처해 있다.브라우저의 호환성이 높아지며 프레임워크는 데이터 바인딩, 단방향 데이터 흐름은 물론 의존성 주입 같은 엔터프라이즈 자바 기능까지 제공했고
이제는 프레임워크 없이도 프론트엔드를 구현할 수 있따.
표준에 집중하면 시간이 지나면서 더 많은 지식을 확보할 수 있다. 매우 효율적인 학습 방법이다. 대중적인 프레임워크를 활용해 보는 것은 재미있겠지만 그렇게 얻은 지식은 다음에 다른 '인기 기술'이 등장하면 소용없는 지식이 되어버린다.

26 자주 릴리스하면 위험을 줄일 수 있다.

위험이란 장애가 발생했을 때 받을 수 있는 최악의 영향과 장애가 발생할 가능성을 결합한 요소다.
규모가 크며 더딘 릴리스가 더 위험하다.
단일 릴리스 패키지에 일련의 변경 사항을 포함해 배포하면 장애 가능성이 커진다. 즉, 여러 사항이 한 번에 변경되기 때문이다.
얼마나 많은 테스트를 실행하든 결국은 성공 여부를 확인할 수 있는 곳은 프로덕션 환경 뿐이다. 작고 빈번한 릴리스는 실패의 가능성을 줄인다.
릴리스에 최대한 작은 변경사항만 포함하면 릴리스로 인해 실패할 가능성이 작아진다.

27 퍼즐에서 제품까지

개발자 경력 초기에는 그저 시스템의 한 부분만을 담당했다. 사용가능한 수단도 제한적이었다. 몇 년이 지나가 나의 시야가 넓어졌다. 이제 문제 해결은 내 업무의 본질이 아니라 전제조건이 되었다. 내 업무의 본질은 타인에게 유용한 제품을 관리하면서 내가 속한 조직에 새로운 역량을 제공하는 것이었다. 문제에는 해결이라는 상태가 정해져 있다.
경력이 더 쌓이자 나의 시야는 더 넓어졌다.
요구 사항을 만족하는 코드를 푸시하는 일은 그저 내 업무의 시작일 뿐이다. 나는 그저 코드를 바꾸는 것 이상을 원한다. 나는 시스템의 변화를 목표한다. 내 앱의 새로운 기능은 내가 담당하는 시스템에 의존하는 현재 시스템에서 동작해야 한다.

'올바른' 제품은 한 가지로 정의할 수 없다. 분명 많은 것이 올바르지 않기에 '오동작'하지 않도록 주의해야 한다. 그 외에는 '더 나은 것'에 초점을 맞춘다.

28 '풀스택 엔지니어'는 마음가짐이다

2007년 이후 개발기술의 상황이 급변했다. 사용자 인터페이스는 점점 더 복잡해지고 이런 복잡성을 다루기 위해 고수준 자바스크립트 프레임워크가 도움되었다. 이제는 NoSQL 데이터베이스를 사용하며 이들은 서로 큰 차이점을 보인다. 카프카로 데이터를 스트리밍하고 래빗MQ로 메시지를 처리하며 그 외에도 여러 기술을 사용한다.

모든 기술을 전문가 수준으로 다룰 수는 없다. 자바 생태계가 얼마나 성장했는지 생각해 보면 자바 전체의 전문가가 되기도 쉽지 않다. 한 가지 위안이 되는 점은 굳이 전문가가 될 필요는 없다는 점이다.

더 중요한 것은 어느 특정 분야로 스스로를 제한하지 않으면 작업을 대하는 태도가 달라진다는 점이다. 누구도 '그건 내 일이 아닌데요' 따위의 말은 하지 않으며 개발자가 더 많은것을 학습할 수 있다.

그러므로 풀스택 개발자는 결국 마음가짐이다. 할수있다는 태도로 시니어이면서 동시에 주니어가 되는 것이다.

29 가비지 컬렉션은 나의 친구

불쌍한 가지 컬렉션 비난만 받고 대접은 받지 못하는 안타까운 자바 영웅 중 하나다.
자바 GC는 필요에 의해 지불해야 하는 비용으로 취급되며 'GC 시간의 단축'은 가장 보편적인 성능 가이드라인이다. 하지만 현대의 가비지 컬렉션은 메모리 할당/해제보다 더 빠르게 동작하며 GC가 실행되는 시간에도 속도를 높일 수 있다. 어째서? 가비지 컬렉터는 메모리 해제 외에 다른 작업도 수행하기 때문이다. 즉, 메모리의 할당과 메모리상 객체 재정렬도 실행한다. 좋은 메모리 관리 알고리즘은 단편화와 경합을 줄여 효과적으로 메모리를 할당한다. 또한 객체를 재정렬해서 처리량은 향상하고 응답시간을 줄인다.

객체지역성은 동시에 사용하는 객체를 메모리상에 서로 가깝게 배치하는 것을 말하며 성능 향상에 도움이 된다.

성능을 측정할 때는 비즈니스 가치와 관련 있어야 한다. 초당 트랜잭션, 평균 서비스 시간 또는 최악의 응답 지연을 최적화하자. 하지만 GC가 소비하는 시간을 너무 세세히 최적화할 필요는 없다. GC가 소비하는 시간은 실질적으로 프로그램의 속도에 도움이 되기 때문이다.

30 이름 짓기를 잘 하자

이름을 더 잘 지으면 무엇보다도 코드의 유지보수성 향상에 도움이 된다. 물론 유지보수가 용이한 코드를 작성하려면 좋은 이름 짓기 외에도 생각할 것이 많다. 하지만 좋은 이름 짓기는 굉장히 어려우며 그래서 대부분 등한시한다.
더 나은 이름을 위한 가이드라인을 도입하자.

  • 단어를 최대 4개 사용하고 약어는 사용하지 말자
  • 문제 도메인의 용어를 배우고 사용하자
  • 복수형은 집합명사로 대체하자
  • 엔티티 쌍의 이름은 관계의 이름으로 대체하자
  • 클래스와 객체 이름을 혼합하지 말자

31 이봐 프레드, 해시맵 좀 전해 주겠는가?

기술적인 클래스 이름이 우리가 모델링하는 도메인의 어휘와 일치하는 경우는 드물다.

32 널을 피하는 방법

토니 호어는 널을 '10억 달러짜리 실수' 라고 부른다. 널은 실수가 맞다.
NPE를 회피할 수 있는 안전한 방법은 가능함녀 다른 방법을 사용하는 것이다.

  • 변수를 널 값으로 초기화 하지 말자.
  • 널 값을 리턴하지 말자.
  • 널 값 매개변수를 전달하거나 받지 말자.
  • 허용할 수 있는 널 값

33 JVM의 크래시를 유발하는 방법

JVM에 크래시를 유발하는 방법을 찾아 보자.

  1. 최대한 많은 메모리를 할당해 보자. RAM은 무한하지 않다. 더 할당할 RAM이 없으면 메모리 할당이 실패한다.
  2. 하드디스크에 여유 공간이 없어질 때까지 데이터를 기록해 보자. RAM과 같은 문제가 발생할 것이다. 물론 하드디스크는 RAM보다 크지만 디스크 공간도 무한하지 않다.
  3. 최대한 많은 파일을 열어 보자. 여러분의 환경이 지원하는 파일 서술자의 최대 개수를 알고 있는가?
  4. 최대한 많은 스레드를 생성해 보자.
  5. 파일 시스템의 .class 파일을 직접 수정해 보자.
  6. 직접 프로세스 ID를 찾아보자 Runtrime.exec를 이용해 프로세스를 kill 해보자.
  7. System.exit만 호출하는 클래스를 런타임에 생성하고 동적으로 클래스 로더로 로드 한 후 호출해보자.
  8. 최대한 많은 소켓 연결을 생성해 보자.
  9. 시스템을 해킹해 보자.
  10. 안전장치를 건너뛰어 보자.
  11. Unsafe 클래스를 사용해 본다.
  12. 네이티브 코드를 작성해 본다.

34 지속적 전달로 반복 가능성과 감사가능성 향상하기

지속적 전달(CD, Continuous Delivery)은 코드를 프로덕션 환경에 전달하는 단계를 자동화하는 방법이다. 개발자가 변경 사항을 커밋하는 시점부터 해당 변경 사항이 프로덕션 환경에 배포될 때까지의 모든 과정에 포함된 테스트, 변경 제어, 배포 처리 등은 반드시 자동화 해야 한다.

반복가능성
코드 배포 절차를 자동화한다는 것은 각 단계를 스크립트화해서 사람 대신 사람 대신 컴퓨터가 실행하게 하는 것이다. 반복 작업의 실행은 사람보다 컴퓨터가 훨씬 나으므로 배포 단계의 반복 가능성이 크게 향상된다.

감사가능성
배포를 자동화하면 투명성이 크게 향상되어 본질적으로 감사가능성이 향상된다.

35 자바는 자바만의 강점이 있다

36 인라인식 사고

37 코틀린과의 상호운용

최근 몇 년간 코틀린으니 JVM 커뮤니티의 뜨거운 감자였다. 코틀린의 장점 중 하나는 자바와의 뛰어난 상호운용성이다.
하지만 어떤 시나리오에서는 API에 작업을 약간만 추가해도 다른 언어에서 훨씬 더 쾌적하게 사용할 수 있다.

38 일은 끝났어요. 그런데...

  1. 의사소통과 명료성
    이상적이라면 여러분의 팀은 끝에 대한 정의가 있어야 한다.

  2. 인지(perception)
    관리자는 이라는 단어를 좋아한다. 이 말은 이제 여러분이 다른 일을 더 하거나 다른 팀원을 도울 여력이 생겼다는 의미다.
    이제 첫 번째 작업을 완전히 끝내지 못한 상태에서 다른 작업을 시작했다고 생각해 보자. 바로 이 시점에 기술 부채가 생겨나는 것이다.

  3. 부분적인 완료 따위는 없다.
    끝이라는 것은 이분법적 상태다. 즉 끝냈거나 끝내지 못한 상태만 있을 뿐이다. 부분적으로 끝낸 상태 따위는 없다.

반드시 기억하자.
실제로 끝내기 전까지는 절대 끝냈다고 말하지 말자.

39 자격증 : 기술 업계의 터치스톤

자바 자격증은 개인의 경력 향상에 도움이 된다. 사람이 작업을 찾거나 조직과 팀이 검증된 기술을 갖춘 인재를 찾을 때는 이 자격증을 고려하는 것이 첫 번째 단계일 것이다.

40 자바는 90년대생

어쩌면 기본자료 타입(primitive type)의 개념은 성능 향상을 위한 방책이자 새로운 언어인 자바에 입문하는 C++프로그래머가 조금 더 친숙함을 느끼게 하기 위한 장치였다고 생각할 수 도 있다.
2020년 시점에서, 자바는 주류이며 중도적인 언어처럼 보인다. 하지만 한 가지 놓친 부분은 소프트웨어 세계가 자바의 등장을 기점으로 급진적으로 변했다는 점이다.
가상 머신, 동작 자기관리, JIT 컴파일, 가비지 컬렉션 같은 굵직한 아이디어가 이제는 프로그래밍 언어의 보편적인 환경을 구성한다.

41 JVM 성능 관점에서의 자바 프로그래밍

  1. 가비지에 대한 강박을 버려라
    적응형 컴파일러를 신뢰하되, 의심스러운 부분이 있다면 내구성, 관측가능성, 로깅, 그 외 자바의 풍부한 생태계에서 구할 수 있는 다른 도구를 이용해 검증하자.
    GC에 중요한 것은 객체의 생존/수명, 객체의 사용 빈도, 애플리케이션의 실제 크기, 오래 지속되는 임시 객체, 할당률, 마킹 오버헤드 등이다.

  2. 벤치마크를 특정 지어 검증하라
    '테스트 단위'를 특정하고 테스트 결과에 부정적 영향을 미칠 수 있는 다른 컴포넌트와 격리하는 데 도움이 된다.

  3. 할당 크기와 비율이 중요하다.

  4. 적응형 JVM은 권리이자 필요다.

42 자바는 재미있어야 한다.

43 자바의 불분명한 타입들

대체 널(null)이 무엇일까?
자바의 모든 값은 타입을 가지므로 null 역시 타입을 가져야 한다. 그렇다면 어떤 타입이어야 할까?
자바 언어 명세 4.1절에서 그 답을 찾을 수 잇는데

자바는 특별한 null 타입을 지원한다. 타입은 이름을 갖지 않는 널을 표현한다. null 타입은 이름이 없으므로 null 타입의 변수를 선언하거나 null 타입으로의 타입 변환은 불가능하다.

그렇다. 자바는 변수 타입을 선언할 수 없는 타입의 값을 사용하는 것을 허용한다. 이런 타입을 '불분명한 타입' 이라고 하며 예전에는 '표시할 수 없는 타입' 이라고 불렀다.

44 JVM은 멀티패러다임 플랫폼이다

자바는 명령형 언어다. 즉 자바 프로그래머는 JVM이 무슨 동작을 언제 할 것인지 코드로 명령한다.
자바 8이 등장하면서 혁신적인 변화가 이루어졌는데, 메서드 참조, 람다표현식, 인터페이스 기본 메서드, 고차 함수, 암묵적 반복 등 많은 기능이 추가되었다.
객체지향과 함수형 프로그래밍은 서로 대치하는 전개에서, 자바 8이 등장하면서 멀티 패러다임 언어가 되었다.

45 최신 동향을 파악하자

자바 프로그래머로 살아남기 위해서는 자바가 정체된 언어가 아니라는 점을 받아들여야 한다. 자바는 단순히 버전만 올리는 것이 아니라 라이브러리, 프레임워크, 심지어 새로운 JVM 언어로 계속 발전하고 있다.
최신 동향을 파악하는 것은 시장에 있는 모든 기술을 알아야 한다는 의미가 아니다. 그저 계속 동향을 파악하면서 공통 키워드에 주목하고 기술 트렌드를 이해하면 된다. 더 깊이 공부하는 것은 현재 수행중인 업무와 관련이 있거나 개인적으로 흥미가 있을 때 해도 늦지 않다.

46 주석의 종류

  • 계약은 자바독 주석으로 작성한다
  • 문맥은 블록 주석으로 작성한다.
  • 이상한 점은 줄 단위 주석으로 작성한다.

47 은혜로운 flatMap

보편적인 프로그래머와 데이터 엔지니어는 모두 flatMap을 이해해야 한다. map이나 filter와 마찬가지로 flatMap은 Stream나 completableFuture처럼 '뭔가는 담는 컨테이너' 객체와 함께 사용할 수 있다.
Stream 타입을 사용하든 C 타입을 사용하든 forEach 메서드나 collect 같은 종결형 연산자를 사용하기 전에 map, filter, flatMap, groupBy 등의 메서드로 데이터를 처리하자.
종결형 연산자를 제대로 사용하지 못하면 자바 스트림, 스트리밍 라이브러리 또는 빅 데이터 프레임워크가 제공하는 지연성과 최적화의 장점을 활용하지 못할 것이다.

48 컬렉션을 제대로 이해하자

컬렉션은 모든 프로그래밍 언어에서 중요한 요소다.
컬렉션은 순서가 있는(ordered) 것과 순서가 없는(unordered) 것으로 구분할 수 있다. 순서가 있는 컬렉션은 순회 순서를 예측할 수 있지만 순서가 없는 컬렉션은 그럴 수 없다.
클래스를 구분하는 또 다른 방법은 정렬도니 것과 정렬되지 않은 것으로 나누는 방법이다.
List는 안정적인 인덱스를 기반으로 순서가 있는 컬렉션을 구현한 인터페이스다. 리스트에는 중복된 값도 삽입할 수 있으며 아이템의 순회 순서를 예측할 수 있다.
Map은 키와 값의 관계를 유지하며 유일한 키 값만 보관하는 인터페이스다.
Set은 유일한 아이템의 컬렉션을 정의하는 인터페이스다.

49 코틀린은 정말 물건이다.

아마도 자바는 지금까지 보편적으로 사용하는 언어 중에서도 가장 성숙하면서도 검증된 언어겠지만 그래서 향후에 변할 가능성이 없는 언어다.
그래서 일부 똑똑한 사람들은 최신 프로그래밍 언어가 갖춰야 할 개념을 적용하면서도 자바가 할 수 있는 모든 것을 수행함은 물론 쉽게 배울 수 있고 상호운용성도 높은 새로운 언어를 개발했다.
코틀린은 더욱 짧고 간결하며 현대적인 코드를 작성하기 위해 개발된 언어다.
코루틴은 호출 코드로부터 비동기 방식으로 작업을 실행한다.

50 관용적인 자바 코드를 학습하고 머리속에 캐시하자

루핑, 조건, 스트림 같은 일부 관용 코드는 모든 자바 프로그래머에게 익숙하다.
관용 코드에서 주목할 만한 점은 항상 의도를 가지고 학습하는 것이 아니라는 점이다. 뇌를 캐시처럼 사용하라는 말이다.
관용 코드와 공통 라이브러리 API를 익혀 두자. 나머지는 어디서 빠르게 찾을 수 있는지 알아 두자.

51 카타를 하기 위해 학습하고 카타를 이용해 학습하자

코드 카타는 실습으로 특정한 기술을 연마하는 실천적인 프로그래밍 기법이다.
코드 카타를 직접 만들어 보고 싶다면 다음 절차를 따라 해 보자.

1. 학습하려는 주제를 선정한다 2. 원하는 지식을 설명할 수 있으며 성공하는 단위 테스트를 작성한다. 3. 최종 솔루션에 만족할 때까지 반복해서 코드를 리팩토링한다. 리팩토링 과정에서 단위 테스트가 실패하지 않는지 확인한다. 4. 테스트가 실패하도록 실습한 솔루션을 삭제한다. 5. 실패하는 테스트와 고나련 코드 그리고 빌드 결과물을 버전관리 시스템에 커밋한다. 6. 소스 코드를 공개한다.

여러 분이 학습한 것과 지식을 가치 있게 여기자. 뭔가 유용한 것을 배웠다면 기록으로 남기자. 자신이 실습한 코드가 어떻게 동작했는지 되돌아보기 위해 기록을 남겨둔다면 향후에 매우 유용할 수 있다.
우리는 여전히 많은 것을 배워야 하지만 가르칠 수 있는 것도 많다. 학습한 것을 다른 사람과 공유함으로써 전체 자바 커뮤니티를 개선하는 것이다.

52 레거시 코드를 사랑하는 방법

레거시 시스템이란 유지보수와 확장, 개선이 매우 어려운 오래된 소프트웨어를 뜻한다. 반면, 이미 제대로 동작하고 있으며 비즈니스가 돌아가도록 지원하는 소프트웨어를 의미하기도 한다.
레거시 시스템을 유지보수 해야 한다면 어떨까? 레거시 시스템의 버그를 수정해야 한다면 어떻게 할 것인가?

  1. 첫번째 해결책은 테이프로 감는 것이다. 숨은 꾹 참고 결함을 수정한다. '언젠가 후회할 지 모르겠지만 우선은 복사-붙여넣기로 문제를 해결하자!' 라고 생각하는 순간부터 시스템은 더 악화한다.

  2. 두번째 해결책은 예전 시스템은 그만 잊어버리고 새로운 시스템을 바닥부터 다시 개발하는 것이다. 이 방법의 문제점이 무엇인지 이해하겠는가? 대부분 재개발은 제대로 진행되지 않거나 끝이 나지 않는다.
    그 이유는 생존자편향때문이다.

그렇다면 어떻게 해야 할까?
어쩌면 우리는 레거시 코드를 잘못된 관점에서 바라보고 있는지도 모른다. 제대로 수정하는 방법을 배워야 한다.
교살자 패턴을 이용하면 간단하다. 좋지 않은 코드를 깔끔하고 철저히 테스트한 새 코드로 교체하는 것부터 시작할 수 있다. 그런 후 이 작업을 반복해서 예전 애플리케이션 위에 새로운 애플리케이션을 만들어 가는 것이다.
이 작업을 새 코드로 오래된 코드를 완전히 교체할 때까지 계속하면 된다.

설령 완전히 끝내지는 못하더라도 오래된 코드가 계속 썩어가도록 두는 것보다는 새로운 코드와 오래된 코드가 섞여 있는 편이 낫다. 게다가 완전히 새로운 시스템을 처음부터 개발하는 것보다 안전하다.
왜냐하면 새로운 기능을 계속해서 검증할 것이며 버그가 발견되면 이전 버전으로 롤백이 가능하기 때문이다.

레거시 코드는 조금 더 사랑받을 자격이 있다.

53 새로운 자바 기능을 학습하자

자바 8에서 도입한 람다와 스트림은 자바 프로그래머의 언어 구조를 크게 변화시킨 두 가지 핵심기능이었다. 자바 9이후부터는 6개월마다 새 버전을 릴리스하며 그때마다 새로운 기능을 도입한다. 새로운 기능을 활용하면 더 나은 코드를 작성할 수 있으므로 어떤 기능이 추가되었는지 눈여겨봐야한다.

54 IDE를 활용해 인지 부하를 줄이는 방법

IDE가 제공하는 기능을 학습하고 업무에 활용하면 생산성을 한 단계 더 높일 수 있다.

  • 코드를 자동생성하므로 여러분이 직접 입력할 필요가 없다
  • 컴파일러 에러를 유발하지 않음녀서 코들르 특정한 방향으로 자동 이동해주는 리팩토링 도구를 제공한다
  • 테스트를 실행하며 문제를 디버깅하는 데 필요한 기능을 제공한다
  • 빌드 및 의존성 관리 시스템과 통합되어 개발 환경을 테스트 및 프로덕션 환경과 같은 방법으로 동작하게 한다.
  • 버전 제어, 데이터베이스 접근, 코드 리뷰 등 애플리케이션 이외의 도구와 시스템을 활용한다
    코드를 작성하기 위해 여러분이 선택한 도구는 여러분이 오롯이 개발에만 집중하도록 도와야 한다.
    코드를 작성하는 데 필요한 다른 복잡한 것에 주의를 빼앗겨서는 안 된다. 잡다한 것은 IDE에 맡겨서 인지 부하를 줄이면 여러분이 해결하려는 비즈니스 문제에 더 집중할 수 있다.

55 자바 API를 디자인하는 기술

좋은 API는 쉽게 이해할 수 있고 찾아 쓸 수 있어야 한다. 필요할 때 바로 사용할 수 있어야 하며 이상적으로는 문서를 읽지 않고도 그 동작 원리를 학습할 수 있어야 한다.
결국 일관성 있는 이름과 규칙을 사용하는 것이 중요하다.
API를 작게 유지하는 것도 쉽게 사용하게 하는 또 다른 방법이다. API가 작게 유지하면 배워야 할 개념과 유지보수 비용이 줄어든다.
API를 개선할 또 다른 힌트는 크기가 큰 인터페이스를 더 작은 인터페이스로 나누는 것이다. 풀루언트(fluent) API를 구현하는 것을 고려하길 바란다.
마지막으로 처음부터 제대로 된 API를 작성할 수 있다고 생각하지 말자.
API를 디자인 하는 것은 반복 작업이며, API를 개선할 유일한 방법은 개밥 먹기(dogfooding)뿐이다.
API에 대한 테스트와 예제를 작성하고 동료 및 사용자와 끊임없이 소통하자. 몇번이고 반복해서 불분명한 의도, 불필요한 코드, 미처 추상화되지 않은 부분을 개선하자.

56 간결하고 가독성이 좋은 코드

코드는 한 줄 한 줄 모두 최대한 명확해야 한다. 모든 코드는 필요한 것이어야 한다.
가독성이 좋고 간결한 코드를 작성하려면 형식과 내용에 주의를 기울여야 한다.

  • 들여쓰기를 이용해 코드를 정리하자
  • 변수와 메서드에 의미 있는 이름을 채택하자
  • 필요한 경우에는 코드에 주석을 작성하자
  • 주석처리한 코드는 커밋하지 말자
  • 혹시 나중에 필요할지도 모를 코드까지 작성하는 오버엔지니어링의 우를 범하지 말자
  • 장황한 코드를 작성하지 말자
  • 아직 시도해 본 적이 없다면 함수형 프로그래밍을 공부하자
  • 짝 프로그래밍을 도입하자

57 자바를 그루비스럽게

58 생성자에서는 최소한의 작업만

필자는 생성자는 피르뎅 값을 대입하는 역할 만 하는 것을 선호한다.
생성자가 해야 할 일은 올바른 인스턴스를 생성하는 것뿐이다. 객체 생성 시점에 수행해야 할 작업이 더 많다면 팩토리 메서드를 사용한다.
이 방법의 장점을 다음과 같다

- 인스턴스 필드의 수명주기가 매우 명확하다 - 객체 인스턴스의 생성과 사용이 분리되었다. - 생성자와 달리 스스로의 역할을 명확히 표현하는 팩토리 메서드를 선언할 수 있다. - 클래스와 인스턴스의 단위 테스트를 독립적으로 작성하기가 더 쉬워졌다.

이 방법은 클래스 계층 구조상에서 생성자 논리를 공유할 수 없다는 단점도 존재한다. 하지만 헬퍼 메서드를 구현하면 얼마든지 극복할 수 있는 단점이다.
마지막으로 이 방법은 의존성 주입 프레임워크를 다루는 법에 주의해야 하는 이유이기도 하다.
객체의 생성이 복잡하고 리플렉션 기반의 도구를 사용하기 쉽다는 이유로 매개변수를 모두 생성자에 몰아넣는 것은 다시 퇴보하는 것처럼 느껴지기 때문이다.
마찬가지로 '캡슐화' 때문에 비공개로 선언한 필드에 리플렉션을 이용해 직접 값을 대입하는 것은 타입시스템을 망치는 길이며 단위 테스트를 더 어렵게 만들 뿐이다.
필드는 최소 기능 생성자를 이용해 값을 대입하는 것이 옳다.

기껏 80번 항목까지 작성했더니... 날아갔다. 해서 80번까지는 타이틀만.

59 Date라는 이름은 조금 더 명확해야 했다.

60 업계의 발전에 기여하는 기술의 필요성

61 바뀐 부분만 빌드하고 나머지는 재사용하기

62 오픈소스 프로젝트는 마법이 아니다

63 Optional은 규칙을 위반하는 모나드지만 좋은 타입이다

64 기본 접근 한정자를 가진 기능 단위 패키지

65 프로덕션 환경은 지구상에서 가장 행복한 곳이다

66 좋은 단위 테스트에 기반한 프로그래밍

67 OpenJDK 소스 코드를 매일 읽는 이유

68 내부를 제대로 들여다보기

69 자바의 재탄생

70 클로저에 의한 JVM의 재발견

71 불리언 값은 열거자로 리팩토링하자

72 속독을 위한 리팩토링

73 단순한 값 객체

74 모듈 선언에 주의해야 하는 이유

75 의존성을 잘 관리하자

76 '관심사 분리'가 중요한 이유

77 기술면접은 학습할 가치가 있는 기술이다

78 테스트 주도 개발

79 bin 디렉터리에는 좋은 도구가 너무나 많다

80 자바 샌드박스를 벗어 나자

81 코루틴에 대한 고찰

82 스레드는 인프라스트럭처로 취급해야 한다

83 정말 좋은 개발자의 세 가지 특징

84 마이크로서비스 아키텍처의 트레이드오프

85 예외를 확인하지 말자

86 컨테이너로 통합 테스트의 숨겨진 가능성을 끌어내자

87 퍼즈 테스트의 어마무시한 효과

88 커버리지를 이용해 단위 테스트 개선하기

89 사용자 정의 아이덴티티 애노테이션을 자유롭게 사용하자

90 테스트를 이용해 더 나은 소프트웨어를 더 빨리 개발하자

91 테스트 코드에 객체지향 원리 적용하기

92 커뮤니티의 힘을 빌려 경력을 개발하자

93 JCP 프로그램에 대한 이해와 참여 방법

94 자격증에 가치를 두지 않는 이유

95 주석은 한 문장으로 작성하라

96 '읽기 좋은 코드'를 작성하자

97 젊은 객체, 늙은 객체, 그리고 가비지

반응형