TIL-20210627~0703

2021. 06. 28.

06.27

  • 키패드 누르기가 통과를 못 해서 리뷰를 해보니, double때문에 판단이 제대로 되지 않는 것 같다는 생각이 들었다. 두 점 사이의 거리를 계산해야되는데, 슬빈님 코드를 보고 다시 생각해보니 정확한 거리를 측정하는 것이 아니기 때문에 거리 공식까지 필요없고 x축 거리와 y축 거리를 합친 만큼을 비교하면 되는 것이었다. 문제를 다시 보니 문제에도 힌트가 있었는데, 아쉽다.

    땅따먹기는 전형적인 DP문제 같아 내가 골랐다. 다른 분들 눈에는 그렇게 보이지 않았나 보다... 삼각달팽이는 풀이를 들어봤는데, 구현을 잘 하면 된다. 슬빈님은 아예 삼각형을 하나의 배열에 표현을 했는데 잘 생각해보고 풀어야겠다.

06.28

느긋하게 쉬엄쉬엄 보냈다. 주말에 하지 않았던 공룡책 정리를 했는데, 암묵적 스레딩이라는 파트다. 내용이 이해하기 어렵진 않은데, 양이 엄청 많다. 중요한 부분인가보다. 우선 나온 부분은 스레드풀인데 잘 알고 있는 내용이라 크게 어렵진 않았다. 이전 파트에 이어서 자바의 스레드를 활용하는 방법도 나오는데, 이는 추후에 읽으며 정리해봐야겠다.

06.29

  • 포크조인도 라이브러리에서 해주면 암묵적 스레딩이라 할 수 있다고 한다. 자바의 스레드패키지나 퓨처처럼 OpenMP라는 C, C++ 환경에서 쓰이는 스레드 라이브러리가 있다고 한다. 저수준에 가까운 언어들 답게 스레드를 수동으로 조정할 수도 있다고 한다.
  • 히로와 얘기를 하다가 api와 런타임에 대해 얘기했다. 발단은 JUnit의 모듈이 이해가 되지 않는 다는 것이었다. 구조를 보니 3, 4와 같은 레거시 코드는 vintage 엔진, 5는 jupiter 엔진으로 구동된다. 우선 인터페이스는 말 그대로 접점이다. 하드웨어적으로는 usb 인터페이스, 오디오 인터페이스와 같은 것들이다. 이 것들을 생각해보면 이해하기 쉽다. 소프트웨어적으로는 CLI와 GUI를 생각해보면 이해가 쉬운데, CLI와 GUI는 서로 다른 방식으로 명령을 내리지만, 결국 사용자의 명령을 커널로 전달한다는 같은 기능을 가진다. 즉 공통적으로 들어가는 인터페이스라는 단어에 이런 의미가 내포되어 있는 것이다. 즉 인터페이스란 사용자(클라이언트)와 커널(서버)을 연결시켜주는 부분이라고 할 수 있겠다.

api도 마찬가지다. 맨 처음 생겨난 os의 측면에서 얘기하면 개발자가 사용하기 쉽게 규격을 제공해주는 것이다. 그런데 이 규격도 잘 생각해보면 해당 메소드를 실행하면 커널과 연결시켜 주겠다는 것이다. 다른 API들도 마찬가지다. 우리가 작성한 REST API도 클라이언트와 백엔드 서버 사이를 연결시켜주는 역할을 한다.

다시 돌아와서 JUnit의 API 라이브러리는 사용자의 코드와 엔진 사이를 연결시켜줄 것이다. 런타임은 컴파일 타임과 비교되는데 java 실행을 class 파일로 한다는 것을 알고 있다면 구분할 수 있을 것이다. 즉 api 라이브러리가 인터페이스 역할을 하고 런타임 라이브러리들이 구현체 같은 역할을 한다고 생각하면 된다. 이런 식으로 하면 확장에 열려있는 구조가 되는데 모듈에 이런 식으로 적용하니 신기하다는 생각이 들었다. solid를 설명하기 좋은 예시인 것 같다.

06.30

카카오페이 과제 1일차

카카오페이 과제를 시작했다. 외부 IDE 없이 제공하는 클라우드 환경에서 개발하는 것인데, 설정에 꽤 애를 먹었다.

  • 도커를 끌 수 없는 문제

    kill 명령어를 이용해야 했는데, fuser나 lsof 같은 유틸이 설치 되어 있지 않아 netstat으로 직접 파싱했다.

  • 껏다 켜면 vscode가 초기화 되는 문제

    settings 파일을 수정해서 해결했다. extension이 날아가는게 제일 짜증나는 문제였는데, 처음에는 추천을 해주다가 아예 워크스페이스같은 단위로 권장 설치를 하게 하는 옵션이 있어 설정해뒀다. 어쨌든 다시 실행해야 하는 것은 변하지 않지만, 이 정도면 처음에 비해서는 훨씬 낫다.

  • 포매팅 문제

    구글링 해서 메소드체이닝을 깔끔하게 하는 법을 알아내긴 했는데, 인덴트가 계속 8로 변화해서 애를 먹었다. 이걸 찾지 못해서 계속 삽질을 했는데... 왜 그런건지는 이유를 도무지 모르겠다.

가장 큰 문제는 웹 기반이라 컨트롤 pgup 혹은 pgdn으로 탭을 이동하는 단축키가 중복으로 먹는다는 것이다. 열받는 포인트는 f5는 디버깅으로 잘 작동한다... alt 좌우 이동으로 키를 변경해도 뒤로가기, 앞으로가기와 겹친다. 이건 어떻게 해결을 못 하겠다.

그래도 설정을 좀 잡으니 꽤 쓸만해졌는데, 이대로 세팅을 옮기면 vscode로 실제 개발도 할 수 있을 것 같다. 또 하나의 벽은 테스트 클래스 자동 생성 기능인데, 일단은 파일 생성을 좀 쉽게 하는 방법이 있는지를 찾아봐야겠다.

07.01

카카오페이 과제 2일차

이전에 했었던 시행차고들을 뒤로 하며 목업 api 이후 바로 도메인 로직으로 넘어갔다. ORM을 왜 사용하는 가를 생각하다 보니 생각 정리가 조금 됐다. 이전에도 얘기했던 적이 있었지만 다시 얘기해보자면, 마이 바티스나 JDBC템플릿같은걸 직접 사용하면 쿼리를 짜야한다는 단점이 있지만 설계는 훨씬 좋다. 반면 ORM같은 프레임워크를 쓰면 해당 프레임워크의 사상에 맞춰서 객체 설계를 하도록 강제된다. Spring Data JDBC가 DDD를 엄격하게 준수하도록 제한한다면, JPA는 조금 느슨하다. 양방향 매핑 때문인데, 어찌됐건 객체지향적으로 설계를 하도록 유도하는 것은 마찬가지다. 그렇다면 가장 중요한 것은 어찌됐건 객체다. 성능에 크게 문제가 되는 것이 아니라면 데이터보다는 객체로 처리하도록 하는게 JPA를 사용하는 의도와 맞지 않나 싶다. 그래서 도메인으로 할 수 있는 작업은 모두 도메인에서 처리하도록 생각해봤다.

너무 당연한 것이긴 하지만, 그렇게 생각 정리를 하니 의외로 설계가 엄청 단순해졌는데, 예전에 DTO와 엔티티를 객체의 스냅샷으로 생각하기로 한 것도 큰 도움이 된 것 같다. 레포지토리 작업을 제외하고 일단 객체를 이용하여 작업을 다 한 뒤, 원하는 순간에 객체의 스냅샷을 DB에 저장하도록 한다. 물론 JPA에는 영속화라는 개념이 있긴 하지만, 직접 구현하지 않고 Spring Data JPA를 사용할 것이므로 좀 더 심플하게 생각할 수 있었던 것 같다. 아직 잘 모르고 하는 소리일 수도 있지만, 영속화 후 변경감지같은 것들이 굳이 필요한가에 대한 의문도 조금 들긴 한다. 조금 더 자연스러운 흐름을 위해서인지...

어쨌든 오늘은 로직들을 정리하고 구현하는데 시간을 썼다. 조금 어려웠던 점은 id는 모두 인공키를 사용하기로 했던 점인데, 그러다보니 equals 정의에서 헷갈리는 부분이 생겼다. 실제로는 유니크 컬럼을 기준으로 생각해야 하지만 인공키가 주 키 역할을 하기 때문인데, 이 부분에서 많은 고민을 했다. 예를 들어, 사용자 id가 있다면 이 것을 주 키로 사용하는 것이 맞지 않은가? 하는 것이다. 우선 성능상 이점이나 개발용이성을 다 차치하고 생각해보면 사용자 id는 언젠가는 변할 수 있는 값이다. 예를 들어, 사용자 id는 절대 변하지 않는 값이라 정의를 해뒀어도 언젠가는 변할 수 있다. 그럴 일은 없어요~ 라고 해도 요구사항이란 언젠가는 바뀔 수 있다. 하지만 인공키는 그렇지 않기 때문에 의미가 있지 않나 하는 생각을 했다. 그 외에 클러스터링 인덱스 때문에 순차적이지 않을 경우 문제가 발생할 수도 있다고 하는데, 이는 어차피 UUID같은 것을 사용해도 발생할 수 있는 문제일 것이라 생각된다. 자세한건 예전 호눅스 수업을 리마인드하며 다시 생각해봐야 할 것 같다.

07.02

  • 팩토리 메소드. 아주 많이 쓰인다. 장점이 몇 개 있지만, 사실 생성자 대신 사용하는 것은 캡슐화 정도가 뚜렷한 장점이라 할 수 있겠다. 그런데 이 것도 사실 엄밀히 따지면 캡슐화만으로는 장점이라 하기 애매하다. 더군다나 빌더가 사용하기 더 편리하다는 점에서 더 그렇게 느껴진다. 개인적으로는 그런 관점 보다는 좀 더 크게 봐야 장점이 보이는 것 같다.

    반대로 생성자에 로직이 담기면 좋지 않은가? 그럴 가능성이 있다. 생성자 뿐만 아니라, getter나 setter 등 또한 마찬가지일 것이다. 단순한 자바 프로그래밍에선 생성자에 들어갈건 생성자에 넣어주는 것이 좋다. 하지만, POJO나 자바빈 같은 기본적인 규약을 지켜야하는 경우가 많다. 오래 된 언어라서 그렇기도 하지만, 어쨌든 라이브러리나 프레임워크에서 해당 규약들을 바탕으로 구현하는 경우가 많고, 리플렉션 또한 비슷하게 사용한다. 즉 자바빈 규약에 맞춰 만들어진 메소드들을 바탕으로 움직이는 것들이 많다는 것이다. 당장 스프링만 봐도 그렇다. 스프링 자체가 그렇다고 하기엔 애매하지만 잭슨이나 ORM 등과 같은 것들은 해당 규약을 적극적으로 사용한다. 즉, 특정 부분을 위해 커스텀하거나 로직이 들어간다면 다른 곳에서 원치 않는 결과가 나올 가능성이 생기는 것이다.

    이런 관점에서 보면 팩토리 메소드의 장점이 보인다. 생성자는 순수한 생성의 역할만 하도록 하고 감춘 뒤, 추가적인 역할은 팩토리 메소드에서 하도록 하면 된다. 같은 이유로 getter나 setter에서 사용되는 접두사를 사용해서 네이밍을 하지 않는 것이 좋다. 뿐만 아니라 getter와 setter는 필드에 직접 접근 하는 것과 같은 느낌이다. 무분별한 getter나 setter를 지양하자는 것도 private 필드를 public하게 만들기 때문이라는 생각이 든다.

카카오페이 과제 3일차

  • 도메인 로직을 다 완성하고 간단한 리펙토링을 먼저 했다. 우선 적용 한 것은 enum을 사용하는 것이었는데, 문제는 DTO와 엔티티에서 어떻게 표현할지였다. JPA로 보낼 때는 컨버터를 작성하면 된다고 한다. 문제는 컨트롤러였는데, 방법은 여러가지였다. 가장 바람직해보이는 것은 DTO작성이지만, enum은 자체로 생각하고 있기 때문에 굳이 그렇게까지는 하지 않고 json value로 처리해줬다.
  • 설계 미스가 살짝 있었는데, 관계 테이블을 간과하고 있었다. 하나의 aggregate로 묶어서 생각하고자 했기 때문에 놓친 부분이었다. 때문에 그 사이의 매핑 클래스를 하나 뒀다. JPA에 다대다 관계를 처리해줄 수 있는 어노테이션이 있긴 한데, 관계 테이블에 비즈니스 데이터가 묶여있기 때문에 단순히 매핑만 해줘서는 해결이 되지 않는 상황이다. 따라서 변경해줬는데, 테스트가 있어서 그나마 나았지만 시간을 꽤 잡아먹었다.
  • JPA는 엔티티 연결이야 RDB와 같으니 크게 어려움이 없었는데, 테스트를 하며 문제가 발생했다. LinkedSet을 제대로 불러오지 못해 꽤 애를 먹었다. LinkedSet으로 초기화 해주도록 했음에도 불구하고 인스턴스가 PersistenceSet으로 만들어지는 것이다.

    expected field is an ordered collection but actual field is not (org.hibernate.collection.internal.PersistentSet)

    일단은 타입까지 검증하지 않고 내용물만 검증하도록 했다. 그냥 넘어갈까 하다가 찝찝함에 일단은 10개 정도까지 순서 보장을 테스트 해보기로 했다. 결론적으로 순서가 보장되는 것 같긴 한데, 검증 과정에서 문제가 발생했다.

    계속해서 id 1번이 없다는 에러 메세지가 나왔다. DB에 해당 컬럼이 없다는 얘기인데, 아무리 해도 잘못된 부분을 찾지 못하겠어서 디버깅을 해보니 두 번째 테스트 셋을 저장하면 id가 4번부터 시작한다. 바로 auto increment 초기화가 되지 않아서다. 쿼리로 해결할 수 있긴 하지만, db에 종속적이기 때문에 좋지 않다고 판단, 다른 방법을 찾아봤다. @DirtiesContext 라는 어노테이션인데, 원하는 수준에서 컨텍스트 초기화를 시켜준다. 단점은 비용 즉 시간이다. 허나 메소드에도 적용가능하기 때문에 현재 나의 상황에서는 크게 문제가 없다고 판단, 바로 적용했다.

생각보다 진도가 느려졌다. 빨리 해서 마이그레이션 툴 같은거도 공부하면서 잡아나가볼까 했는데, 시간이 촉박할 것 같다. 그래도 벌써 배운게 많은 것 같아서 좋다.

07.03

  • 카카오페이 과제 덕에 TIL이 엄청 길어졌다. 첫 부분에 toc를 달아주면 어떨까 하는 생각이 든다.

카카오페이 과제 4일차

  • 레포지토리는 DataJpaTest로 변경했다. 예전에 잘 동작하지 않았던 기억이 있어 나중에 적용할까 하다가 오전동안만 해보려고 서칭을 해봤다. 예전에 에러가 났던 이유는 H2 없이 작동시켰기 때문이었던 것으로 생각된다. 해당 경우는 현재 사용중인 DB를 사용하도록 설정을 변경해줘야 한다. 만약 아닐 경우 h2로 인메모리 db를 자동으로 설정하여 실행시켜준다. 트렌젝션 어노테이션도 포함되어있다. 사실 프로젝트가 너무 작아서 속도에 큰 영향은 없겠지만, 어쨌든 하나를 잘 클리어한 느낌이다. 그런데 코드는 그렇지 못한 것 같은데, 레포지토리 자체를 검증한다기 보다는 엔티티 매핑과 쿼리 생성이 내 의도대로 잘 되는지를 테스트한 것 같다. 그런 부분을 어디선가는 테스트해야 할 것 같긴한데, 여기가 맞는 것인지는 모르겠다. 통합테스트에 포함이 되어야 하는 부분들인지...
  • 예외와 boolean에 대해 생각을 좀 해보게 됐다. 요구사항에서는 실행이 제대로 됐을 경우 true를 반환하도록 하는 api가 있었는데, 처음에는 제대로 실행되지 않으면 에러로 처리되던가(400이나 404 등) 그렇지 않다면 DB오류가 아닌가? 하는 생각이 들었다. 따라서 의미가 있는지 의문이 많이 들었는데, 계속 하다보니 실제로 가능한 케이스도 예외처리를 하고 있는 것을 발견했다. 만약에 실제 서비스 중에 해당 경우가 발생한다면 계속 에러가 발생할텐데 우리야 클라이언트에게 책임을 넘기는 것 처럼 해놓았지만, 실제 서비스에서는 일어나면 안 되는 일이다.

    조금 더 범위를 좁혀서 업데이트의 경우를 생각해보자. 만약 실제로는 들어올 수 있지만 사용되어서는 안 되는 값이 들어왔는데, 이를 예외처리하지 않는다면 아무런 변화가 생기지 않을 것이다. 물론 어떻게 짜느냐에 따라 다르겠지만 나의 경우는 그렇다. 이런 경우를 t/f로 표현해줘야 한다는 생각이 들었다. 처음에는 save나 delete 개수 등을 카운팅해야하나? 하는 생각도 해봤지만 해당 경우는 예외로 처리되어야 하는 것 같고, 저런 식으로 논리적인 부분을 따지는 것이 맞는 것 같다. 해당 기능이 실행된 것이 맞는지.

  • 스웨거 없이 개발해보니 컨트롤러 단위테스트가 왜 필요한지 감이 좀 오는 것 같다. 목업 api에 스웨거를 달아놓으면 비록 수동이지만 컨트롤러에 대한 단위테스트가 가능했던 것이다. 그렇게 생각을 하니 어떻게 테스트를 해야할지 명확해졌는데, 요청, 응답 잘 되는지, 헤더나 컨텐츠 타입 등 검증. http status 검증. 에러 처리 검증이나 응답 데이터 형식이 내가 생각했던 것과 같은지(정적인 값이면 비교해보는 것도 좋겠지만, 여기서는 구조와 타입같은게 더 중요한 것 같다). 등이다.

TODO

  • MockMvc 분석

    • 테스트 기본 인코딩이 이상함.
  • 잭슨 리퀘스트 바디 파싱 분석

    • request시에 생성자 인식 못 함.
  • 스프링 절대경로 서버주소 어떻게 인식하는지(어떻게 nginx 주소를 알 수 있나)?
  • 우아한 객체지향
  • 이런 REST로 괜찮은가
  • 알고리즘

  • OS

    • 4.5 + 문서 정리
    • 기본 개요 정리
    • 추가 내용 정리
    • 4.6
  • AWS 강의듣기

    • IAM 정리하기
  • 데브독스 넥스트(7월 첫주)
  • 엘라스틱서치
  • 서브넷 구분
  • s3 이용

    • 구현하기
  • 깃헙액션으로 aws 배포
  • classForName 테스트
  • sticky session
  • clustered index
  • ACID
  • 블로그

    • 디렉토리 구조 수정
    • blogs/{yyyy}/{mm}/{postname}
    • generator 수정 -[ ] 각 분류 별로(til, post 등) 작성할 수 있도록 -[ ] til에 날짜별 구분과 toc 추가해주도록
    • 허스키 수정
    • 현재 파이프라인이 제대로 동작하지 않아서 수정 날짜 후처리가 되지 않음
정대화
DaeHwa_Jeong@outlook.com

Powered with by Gatsby 2.0