지난 1년, 회고

약 1년 전, 2017년 8월 29일에 지금 회사에 입사하였다.

나의 개발자로서의 커리어의 시작은 2016년 10월 4일에 시작되었다.

그 동안을 돌아보는 시간을 가지고 글로 남겨보면 나중의 나를 평가할 때의 기준이 될 수 있지 않을까란 생각이 들었다.

배운 것

스프링과 자바스크립트

지금 회사에 들어오기 전에도 스프링으로 개발을 하고 있었지만, 잘 모르면서 쓰는 것이 대부분이었다. 입사 첫날, 처음으로 주어진 일은 토비의 스프링 책에서 CTO님이 짚어주신 부분 위주로 읽는 것이었다. 그렇게 5일정도를 하루 8시간 토비의 스프링을 읽었고, 그제서야 스프링의 철학과 Application Context, DI, IOC 등의 중요한 개념에 대해서 좀 더 잘 이해하게 된 것 같다. 만약 이 시간이 주어지지 않았다면, 아직도 잘 모르면서 감으로 개발하고 있었을 지도 모르겠단 생각이 든다.

그 다음으로는 더글라스 크락포드의 자바스크립트 핵심 가이드 를 읽었다. 읽으면서 본문에 나오는 모든 코드들을 따라 타이핑하며 읽었는데 개인적으로 자바스크립트에 대한 이해도가 많이 생겼다. 이 역시 이런 시간이 주어지지 않았다면, 감으로 개발하고 있었겠지..?

토이 프로젝트 : 그 유명한(?) 99단계 대댓글 게시판

그 2가지 책을 읽고 난 후, 99단계 대댓글 게시판 구현이라는 2주짜리 숙제가 주어졌다. 단순한 게시판만 짜면 되는 것이 아니라, 생각보다 많은 것을 고려하며 구현해야하는 숙제였고, 1단계부터 3단계가 있었으며 다음 단계로 넘어가려면 이전 단계를 완료하고 확인을 받아야 했다. 고려해야할 사항(+요구사항) 중 기억나는 몇가지를 나열해보면,

  1. 조회 시간복잡도
  2. 삽입 시간복잡도
  3. 페이지네이션
  4. 보안(SQL injection, XSS 등)
  5. 2대 이상의 서버에 배포 고려
  6. 이미지 업로드 (Copy&Paste로도 가능하게)
  7. AOP 메소드 2개 이상 구현
  8. 유닛테스트 3개 이상 작성
  9. 기타 등등…

개발환경 : JPA, Gradle, jQuery, thymeleaf

정도가 있었다.

이 프로젝트를 2주동안 진행하면서 JPA를 처음 쓰게되었는데, 신선했다. ORM, ORM 말만 들었지 직접 써보니 그 편리함에 감탄을 했었다.

개인적으로는 이 프로젝트를 마치고 코드 리뷰를 하는 시간이 있었는데, 아주 유익했다.

내가 짠 알고리즘을 모두에게 설명해야했고, 검증을 받아야 했다.

다른 개발자들이라면 어떻게 짰을지에 대한 피드백도 많이 받았다. 기수 정렬, 버킷 정렬, BFS, Trie 등등 내가 생각하지 못한 개념/알고리즘 등이 등장해 굉장히 신선한 충격을 받았다.

그 이후로 내가 잘 몰랐고, 놓쳤던 부분들에 대해서 다시 한 번 찾아보고, 공부하고 정리를 했는데 이 때 나의 성장 발판이 마련된 게 아니었나 싶은 생각이 든다.

버려진 프로젝트

토이 프로젝트 이후, 과도하게 MSA로 나눠져있던 레거시 어드민을 Monolitic 하게 만드는 프로젝트를 맡게 되었다. 그 프로젝트를 하면서 랜딩 페이지 관련 퍼블리싱이나 가격체계개편 작업 등 유지보수의 업무도 생겼고 무리없이 수행해내며 자신감이 오르고 있었다. 그러나 정작 메인 프로젝트의 진행 속도가 너무 느렸다. 나는 나름대로 계속 DB설계, 코딩 등을 계속 하고 있었는데 지금 생각해보니 삽질을 하는데 대부분의 시간을 썼었던 것 같다. 그렇게 속도가 더뎌진 프로젝트는 결국 12월이 되어서 폐기 처분하기로 결정이 났다. 대신, 5년 넘게 써왔던 사내 업무 시스템을 새로 만들기로 결정을 하였고 이 프로젝트를 맡게 되었다.

신입 개발자로서 프로젝트가 버려진다는 게 어떤 건지 당시엔 잘 몰랐는데, 지금 생각해보면 참 안타까운 일이었고 다시는 그런일이 반복되지 않도록 노력해야겠다는 생각이 저절로 든다.

새로운 프로젝트

새로운 프로젝트를 맡게 되며, 나름의 기술 셋 사용에 있어서 내게 선택권이 조금 있었다. 그리고 곧 리액트 개발자가 입사예정이라고 했고, 나랑 같이 일하게 될 수 있다고 들어서, 뭣도 모르면서 프론트엔드 라이브러리로서 리액트를 사용하기로 결정을 했다.

(처음에는 thymeleaf 로 개발을 시작했었는데, 개인적인 욕심에 리액트를 도입하였는데 이게 추후에 일정 차질을 불러 일으키게 된다…)

리액트를 도입함에 따라 웹팩에 대해서도 아주 많은 삽질을 했거니와 리액트 자체를 공부하는데 아주 많은 시간을 빼앗겨 버렸다. 옆에서 보면 상당히 짜증났을 텐데 티 안내고 기다려주신 팀원 및 CTO께 감사한 마음을 갖고 있다. ㅜㅜ

그래서 프로젝트 초반엔 진행 속도가 너무 느리단 피드백을 수차례 받았다. 나도 잘 하고 싶은데, 생각처럼 빨리 개발하는 것이 잘 되지 않았다. 그래서 많이 depressed 되었고, 내가 과연 개발자가 적성에 맞긴 한걸까란 생각도 많이 하던 시기를 보냈다.

그래도 그렇게 꾸역꾸역 경험이 생기고 익숙해지면 실력이 느는 게 스스로도 느껴졌다, 나름.

그래서 해당 프로젝트는 아직도 진행중(장기 프로젝트)이지만 최근에는 요구되는 일정대로 차근차근 진행이 순조롭게 되고 있는 편이다.

Infra

회사에 오니

  • 소스 형상 관리 : Git
  • branching 전략 : git-flow
  • 빌드/배포 자동화 : 젠킨스
  • DB : Mysql (일부 MariaDB, 일부 Percona)

등등 인프라 스트럭쳐에 관련된 부분에서도 내가 알아야할 부분들이 많았다.

그 중 올해 3월에는 IDC에 있는 5억 rows가 넘는 DB를 cloud로 이전하는 작업도 하게 되었는데, 작업을 준비하면서 mysql 과 replication, 백업과 관련하여 좋은 경험을 쌓을 수 있었고, 재미가 있어서 즐겁게 할 수 있었다. 무엇보다도 이전 당일, 작업을 무사히 마치고 나서 CTO 님과 다른 개발자들께서 이렇게 깔끔하게 제시간에 이전한 건 처음이라고 말씀하셔서 내심 보람을 느꼈다.

그 이후에도 나는 사내에서 신규 DB 구축 및 사내 서버들의 네트워킹 등의 인프라 관련 업무도 적지 않게 수행하게 되었다.

며칠 전엔, replication 이 깨진 DB 를 복구하다 sys 스키마의 functions 가 모두 날아간 이상한 경험도 하게 되었는데, oracle 의 mysql github repository 를 찾아 함수 생성 문을 비교해가며 실행시켜 복구를 성공하였는데 과정이 너무 힘들었지만 그런 것도 개인적으론 재미를 많이 느낀 것 같다.

사내 세미나

2번의 사내 세미나를 진행하였다. 첫번째 주제는 JPA + Spring-data-jpa/QueryDSL 에 대한 소개 및 간략한 사용법이었다. 두번째 주제는 처음에 docker + gitlab ci/cd 으로 스프링 부트 앱 배포하기를 할까하다가, 사내 다른 분이 docker 를 주제를 하려고 한다기에 나는 실무에서 JPA 를 써보며 겪었던 여러 상황들과 해결방법으로 주제를 잡고 진행했다. 다행히 다른 많은 분들이 적극적으로 관심을 가지고 계셨고 도움이 된다고 했다. 진행하면서 약간의 코드리뷰? 같은 게 되버려서 나도 배운 게 많았다. 특히 성능상 fetch type 을 SUBSELCT나 @BatchSize 로 하면서, fetch join을 할 필요가 없다는 것과 DB 인덱스를 고려하여 쿼리를 튜닝하는 등등..

JPA 와 조회 성능, 그리고 Specification

백오피스 웹 애플리케이션 특성상 사내에 쌓이는 데이터에 대한 검색과 관련한 요구사항이 꽤 복잡하고 많았다. 시간이 지나면 지날수록 프로젝트의 사이즈는 점점 커져갔고, JPA 내에서 연관관계를 맺는 entities 의 수가 많아져갔다. 그에 따라 간단한 조회조차 성능이 눈에 띄게 안 좋아지는 것을 겪었다.

이를 해결하기 위해, @OneToOne 관계를 떼어내고, 필요에 따라 hibernate 의 fetch 타입을 변경하고, Specification 에서 fetch 전략을 변경해갔다. 초반에는 대부분 양방향으로 연관관계를 맺도록 했었는데, 프로젝트를 진행하면 할수록 그럴 필요가 없다는 것을 깨닫게 되었다. 이런 저런 경험을 통해 개인적으로 최대한 단방향으로 연관관계를 설정하는 게 좋고, 프론트엔드에서 필요에 따라 역방향이 필요한 데이터를 조회하는 방식으로 진행하는 게 좋은 구조라는 생각이 들었다. 가장 큰 성과(?)는 Entity 의 필드위에 붙이는 어노테이션 중 @xxxToxxx(fetch = ...) 에서 LAZY 와 EAGER 가 있는데 이걸 EAGER 를 LAZY로 바꾼다고 1 + N 문제가 해결되는 게 아닌데, 그동안 좀 헷갈렸었다. 물론 전부 LAZY로 설정해놓고 쓰고 있었는데 곳곳에서 1 + N 문제가 생겨 성능에 큰 영향을 끼쳐지고 나서야 자세히 알아보게 되었다. 1 + N 문제를 해결하려면 fetch 전략에 초점이 맞춰져야 한다. 자바(스프링) 메모리 상에서 프록시로 감싸져있든 말든 fetch 전략이 1개씩만 가져오도록 되어 있으면 1+N 문제가 발생할 것이고, 한 번에 여러개를 가져오도록 되어 있으면 1+N/M 으로 좀 더 나을 것이다. SUBSELECT 로 가져오는 것은 주의해야 하는데, DB에서 Main SELECT를 먼저 조회하고 나서 조건 절 안에 있는 SUBSELECT 문이 실행되고 결과가 나오기 떄문에 총 rows 가 많은 테이블에 대해서는 적합하지 않은 전략이다.

그리고 엔티티의 이력 관리에 대한 니즈도 좀 생겨서 spring-data-envers 를 적용했는데, 1.11.x 버전(스프링 부트 1.5.x 버전)에서는 삭제된 entity 의 revisions 를 조회할 수 없는 버그가 있었다. 그래서 스프링부트의 버전을 2.0.5 로 올리며 또 몇시간의 삽질을 하기도 했었다. 해당 버그는 2.0.1 버전에서 고쳐졌는데 삽질하기 전에 이전 버전에서도 버그 없이 사용이 되도록 PR을 보내봤지만 유지보수 모드에서는 신 기능을 backport 하지 않는다는 답변과 함께 reject 당했다. ㅜㅜ 이건 신 기능이 아니라 버그인 것 같은데.. 버그인 게 확실한데..

다음 1년

다음 1년 동안엔 얼마나 배우고 성장할 수 있을 지 아직 알 수 없지만(6년차 유부남 버프) 끊임없이 노력해야겠다. 그래서 연봉도 빨리 올려서 생활을 안정화 시키고, 디지털 노마드가 될 준비를 다 할 수 있도록 노력해야겠다. TMI. 그렇다, 나의 꿈은 디지털 노마드다. ㅋㅋ 내년엔 어떤 모습의 내가 있을까?

Comments