2021-11-18-TIL
2021-11-18-TIL
Today I Learned
Recruitment Assignment Design
채용 과제를 어떻게 설계하는지 다룬 Collaborne 엔지니어링 블로그 글이다. 면접은 지원자의 면접 실력을 주로 시험할 뿐이라는 문제의식에서, 짧은 전화 면접 뒤에 지원자가 원하는 시간에 푸는 과제와 짧은 Q&A 세션으로 기술 평가를 대신한다. 글이 강조하는 점은 시험할 역량을 처음 시작하는 데 필요한 최소한의 기술과 장기적인 성장 가능성으로 좁혀 정하는 것, 특정 서비스나 방법론을 우연히 알고 있는 지원자가 유리해지지 않게 하는 것, 결과뿐 아니라 질문하는 방식과 불확실성을 다루는 방식처럼 결과를 얻은 과정을 보는 것, 그리고 과제를 Q&A 세션의 토론 재료로 삼는 것이다.
Spring Boot Console Application
웹 서버 없이 명령줄에서 실행하고 끝나는 Spring Boot 애플리케이션을 만드는 방법이다. 메인 클래스나 빈이 CommandLineRunner(또는 ApplicationRunner)를 구현하면 컨텍스트가 준비된 직후 run()이 호출되고, 여기서 DI를 받은 빈으로 작업을 수행한다. 웹 의존성이 없으면 내장 서버가 뜨지 않고, spring.main.web-application-type=none으로 명시할 수도 있다.
- https://www.baeldung.com/spring-boot-console-app
- https://pythonq.com/so/java/133975
- https://www.appsdeveloperblog.com/spring-boot-console-application/
- https://medium.com/nerd-for-tech/running-spring-boot-console-application-under-ide-maven-16b59465a7e0
Multi-Threading in Spring Boot Using CompletableFuture
@EnableAsync를 켜고 서비스 메서드에 @Async를 붙인 뒤 반환형을 CompletableFuture<T>로 하면, 호출이 별도 스레드 풀에서 실행되고 호출한 쪽은 바로 CompletableFuture를 받는다. 여러 외부 호출을 동시에 시작해 CompletableFuture.allOf(...).join()으로 모두 끝나기를 기다리면 순차 호출보다 전체 시간이 줄어든다. 스레드 풀 크기는 ThreadPoolTaskExecutor 빈으로 직접 정하는 편이 안전하다.
- https://dzone.com/articles/multi-threading-in-spring-boot-using-completablefu
Concurrency Control in REST API
두 사용자가 같은 리소스를 동시에 수정할 때, 나중 요청이 앞 요청의 변경을 모르고 덮어쓰는 문제(lost update)를 API 수준에서 막는 방법이다. 링크된 글은 HTTP의 조건부 요청을 이용한 낙관적 동시성 제어를 Spring으로 구현한다. 서버는 응답에 리소스 버전을 담은 ETag를 주고, 클라이언트는 수정 요청에 그 값을 If-Match 헤더로 보낸다. 서버의 현재 버전과 다르면 412 Precondition Failed로 거절한다. JPA에서는 엔티티의 @Version 필드를 버전 값으로 쓸 수 있다.
- https://dzone.com/articles/concurrency-control-in-rest-api-with-spring-framew
Testing Multi-Threaded Code in Java
멀티스레드 코드의 버그는 특정 실행 순서에서만 나타나서 일반 단위 테스트로는 재현이 어렵다. 기본적인 방법은 ExecutorService로 여러 스레드를 동시에 출발시키고 CountDownLatch로 시작과 종료를 맞춘 뒤 결과를 검증하는 것이다. Baeldung 글은 여기에 더해 실행 순서를 체계적으로 바꿔 보는 도구들(tempus-fugit, Thread Weaver, MultithreadedTC, jcstress)을 소개한다.
- https://www.baeldung.com/java-testing-multithreaded
Maven to Gradle
Maven 프로젝트를 Gradle로 옮길 때는 pom.xml이 있는 디렉터리에서 gradle init을 실행하면 된다. Gradle 문서에 따르면 기존 POM을 파싱해 대응하는 Gradle 빌드 스크립트를 생성한다. 링크된 maven2gradle은 같은 변환을 해 주던 도구다. 자동 변환 결과는 출발점일 뿐이라 플러그인 설정은 직접 확인해야 한다.
- https://sagioto.github.io/maven2gradle
Git changing directory
Git은 파일 이름 변경을 별도로 기록하지 않는다. 커밋에는 그 시점의 파일 스냅샷만 남고, git log나 git diff가 삭제된 파일과 추가된 파일의 내용 유사도를 비교해 이름 변경을 추론한다. 그래서 디렉터리를 옮기면서 내용까지 크게 고치면 이름 변경이 아니라 삭제와 추가로 보인다. 이동과 수정을 서로 다른 커밋으로 나누면 이력을 따라가기 쉽고, 한 파일의 이력을 이름 변경 너머까지 보려면 git log --follow를 쓴다.
- https://kwoncheol.me/posts/git-rename-inference
GitHub SSH
GitHub 문서는 ssh-keygen -t ed25519 -C "이메일"로 키를 만들고, ssh-agent에 개인키를 등록한 뒤, 공개키(.pub)를 GitHub 계정 설정의 SSH keys에 추가하는 순서를 안내한다. 이후 git@github.com:사용자/저장소.git 형식의 주소로 비밀번호나 토큰 입력 없이 push할 수 있다. ssh -T git@github.com으로 연결을 확인한다.
- https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent
- https://www.lainyzine.com/ko/article/creating-ssh-key-for-github/
Jekyll Build Time
Jekyll 빌드 시간을 줄인 경험을 다룬 글이다(지금은 주소가 다른 서비스로 넘어가 원문을 확인할 수 없다). 일반적으로는 --profile로 오래 걸리는 템플릿을 찾고, 페이지마다 반복 렌더링되는 include를 캐시하며, 개발 중에는 --incremental을 쓰는 방법이 있다.
- https://forestry.io/blog/how-i-reduced-my-jekyll-build-time-by-61/
Docker Error
Docker Desktop for Mac을 업데이트한 뒤 docker-compose의 볼륨 마운트가 “Mounts denied … file does not exist” 오류로 실패한 이슈다. 경로가 실제로 존재하는데도 실패했고, 이슈 댓글에는 Docker Desktop 이전 버전으로 되돌리거나 실험 기능의 “Use gRPC FUSE for file sharing”을 끄는 우회 방법이 나온다. 이 이슈는 docker/for-mac 저장소의 같은 이슈로 중복 처리되어 닫혔다.
- https://github.com/docker/compose/issues/7990
WebRTC
WebRTC는 브라우저끼리 별도 플러그인 없이 음성, 영상, 데이터를 P2P로 주고받는 기술이다. 연결을 맺기 전에 서로의 미디어 정보(SDP)와 네트워크 후보(ICE candidate)를 교환해야 하는데, 이 교환(시그널링)은 WebRTC가 정하지 않으므로 WebSocket 같은 별도 서버로 구현한다. NAT 뒤의 상대와 연결하려고 STUN 서버로 공인 주소를 알아내고, 직접 연결이 안 되면 TURN 서버로 중계한다. 링크된 글은 이 흐름으로 화상 채팅 예제를 구현한다.
- https://dksshddl.tistory.com/entry/webRTC-%EC%9B%B9RTC-%EC%98%88%EC%A0%9C%EB%A1%9C-%ED%99%94%EC%83%81-%EC%B1%84%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0
Inheritance vs Composition
상속은 “is-a” 관계에서 상위 클래스의 구현을 물려받고, 조합은 “has-a” 관계로 다른 객체를 필드로 가지고 기능을 위임한다. 상속은 상위 클래스의 내부 구현에 하위 클래스가 묶여서, 상위 클래스가 바뀌면 하위 클래스가 깨질 수 있다. ThoughtWorks 글을 비롯한 링크된 글들은 진짜 타입 관계가 있을 때만 상속을 쓰고, 기능 재사용이 목적이면 조합을 우선하라고 정리한다. 조합은 런타임에 구현을 바꿔 끼우기도 쉽다.
- https://betterprogramming.pub/inheritance-vs-composition-2fa0cdd2f939
- https://www.thoughtworks.com/insights/blog/composition-vs-inheritance-how-choose
- https://www.infoworld.com/article/3409071/java-challenger-7-debugging-java-inheritance.html
Jest vs Mocha
JavaScript 테스트 프레임워크 비교다. Jest는 테스트 실행기, 단언, 목, 커버리지를 한 패키지로 제공해 설정 없이 바로 쓰기 쉽다. Mocha는 테스트 실행 구조만 제공하고 단언(Chai)이나 목(Sinon)은 직접 골라 조합하는 방식이라 유연하다. 두 번째 글은 Jest에서 Mocha로 옮긴 이유를 다룬다.
- https://dev.to/heroku/comparing-the-top-3-javascript-testing-frameworks-2cco
- https://if1live.github.io/posts/escape-from-jest-use-mocha/
JUnit Parameterized Test
JUnit 5의 @ParameterizedTest는 같은 테스트를 입력만 바꿔 여러 번 실행한다. 값 목록은 @ValueSource, 여러 인자 조합은 @CsvSource, 복잡한 객체는 @MethodSource로 공급한다. 경계값 여러 개를 테스트 메서드 하나로 검증할 수 있다.
1
2
3
4
5
@ParameterizedTest
@CsvSource({"1, 1", "2, 4", "3, 9"})
void square(int input, int expected) {
assertEquals(expected, input * input);
}
- https://www.swtestacademy.com/junit-parameterized-tests/
Kubernetes and Docker
Docker는 컨테이너 이미지를 만들고 하나의 호스트에서 컨테이너를 실행하는 도구이고, Kubernetes는 여러 호스트(노드)에 걸쳐 컨테이너를 배치하고, 죽으면 다시 띄우고, 부하에 따라 개수를 늘리고 줄이는 오케스트레이션 시스템이다. 경쟁 관계라기보다 Docker로 만든 이미지를 Kubernetes가 운영하는 관계다.
- https://www.openmaru.io/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EC%99%80-%EB%8F%84%EC%BB%A4/
댓글
아직 댓글이 없습니다