오랜만에 블로그에 글을 써보는 것 같다. 취준을 할 때 공부하면서 기록할만한 것들을 기록하던 블로그였지만, 회사에 취업을 하고 난 이후로는 써야지 써야지 하다가 이번에 회사에서 큰 이슈를 맞기도 했고 24년 하반기~25년 상반기에 대해 회고를 남기고자 적어본다.
어쩌다 취업(진짜임)
난 조기취업을 할 생각이 없었으나 요즘 시장이 너무 안 좋기도 하고 빨리 어디든 들어가서 경력을 쌓아야 하나 라는 생각에 밑져야 본전이겠거니 학교를 다니면서 이력서를 몇 군데 넣어보았다. 나의 취준은 본격적으로 취준을 해야겠다는 생각으로 준비를 하진 않았다(이제보면 뭔 자신감으로 그렇게 이력서 넣고 면접 준비도 안 했나 싶다) 근데 그때 이력서를 넣은 곳에서 두 곳에서 연락이 왔다. 한 곳은 스타트업이었고, 한 곳은 지금의 회사다. 스타트업에서는 과제 전형이 있었기에 과제를 제출하고 면접을 보기 전이였고, 한 곳은 면접을 봐보자고 했었다.
이때 당시 면접을 보자고 했었을때 면접 준비를 하나도 하질 않았다. 무슨 자신감이었는지 모르겠다. 그땐 그냥 내가 했던 프로젝트나 공부했던 것들을 말하고 오면 되는 거 아닌가? 면접을 그냥 가서 개발에 대해 대화하는 수준이라고 생각했었다. 근데 실제로 면접을 가보니까 기술 면접이 있었는데 알고 있었는데도 이게 입 밖으로 나오기가 연습을 안 하다 보니 쉽지가 않았다. 분명 아는 건데 이걸 어떻게 설명해야 하지 라는 느낌을 많이 받았다. 어찌어찌 기술면접은 넘어가긴 했는데(뭐라고 대답했는지 기억도 안 남) 그 이후엔 이제 내 경험을 물어보셔서 오히려 긴장이 풀리고 그냥 내가 개발하면서 느꼈던 것들을 다 말하고 왔다.(오래간만에 dev talk 해서 기분은 좋았다) 그리고 협의도 괜찮게 되어서 붙으면 좋고 떨어지면 어쩔 수 없지..(준비 안 했으니까) 이 생각으로 있다가 당일에 연락이 와서 같이 일을 해보고 싶다고 해서 나도 거절할 이유가 없어서 바로 입사를 결정했다. 취업은 진짜 운칠기삼인 것 같다.
JS 혐오자가 Nest.js를 하게 된 것에 대하여
우선, 우리 회사의 테크스펙은 Typescript + Nest.js이다. (스프링도 가끔 한다) 입사할 때 새로운 도전을 해보자는 생각으로 하게 되었다. (면접때 node에 대해서 여쭤봤었는데 아무것도 모른다고 했다) 난 자바를 공부했던 사람이고 학교에서 자바스크립트를 배울 때마다 나랑은 너무 안 맞는다고 생각했었다. 자바에 너무 익숙해져 있었고, 강타입 언어에 익숙하다 보니 타입이 없는 자바스크립트의 높은 자유도는 오히려 나를 헷갈리게 만들었다. 그래서 난 무조건 자바 개발자로 가야겠다 생각을 했지만 Typescript를 접했을 때는 나름 나쁘지 않았다. 내가 마음에 안 들었던 부분들이 싹 사라진 언어라고 생각해서 최근에는 굉장히 흥미롭게 공부 중이다. 그렇게 나는 spring 개발자에서 node 개발자로 처음 시작하게 되었다. 근데 spring으로도 프로젝트를 진행하고 있어서 나름 이도류가 된 것 같다. 이제는 spring 보다 nest가 더 좋은 거 같다. 너무 편하다. 스프링의 설정지옥에서 벗어나 스프링 부트의 auto config를 보고 감탄을 했지만 spring boot -> nest는 더 감탄했다. 코드를 수정하고 서버를 재구동하지 않아도 되는 start:dev의 --watch 옵션은 너무 편했다. 그리고 자바의 null exception에서 벗어날 수 있어서 너무 편했다. 코틀린을 배우면서 느끼기도 했지만, null을 자유롭게 컨트롤할 수 있었고 null safety 한 개발을 할 수 있다는 게 너무 편했다.
동시성이슈(따닥)
내가 맡았던 프로젝트에서 기프티콘 주문이라는 도메인이 있었다. 유저는 앱 내에서 자체 포인트를 모아, 기프티콘으로 교환할 수 있는 일종의 앱테크 앱이다. 이때 유저가 동시적으로 버튼을 따닥 눌러, 주문에 관련된 validation 이 최초 요청에서 넘어가다 보니 다음 요청들도 같이 묶여서 주문 처리가 되어버린 것이다. 프론트에서도 구매 요청 버튼을 여러 번 누를 수가 있었고, 이때 버튼을 누른 횟수만큼 api에 동일하게 요청이 오고 있었다. 이렇기에 락을 걸어뒀어야 했는데, 내가 이 부분은 미쳐 생각을 하질 못했다. 우선 급한 이슈였기에 transaction의 격리 수준을 가장 강한 옵션인 "SERIALIZABLE" 옵션을 걸어두고 hotfix를 했다... 이때 진짜 멘붕이었다.. 실제로 구매 요청이 n건 들어갔고, 피해금액도 생각보다 컸다. SERIALIZABLE 옵션을 걸어두면 가장 높은 격리 수준을 가질 수 있지만, 가장 큰 단점이 있다. 바로 데드락이다. 동시적인 요청이 들어오면 무조건적으로 lock을 걸다보니, 하나의 트랜잭션이 끝나기 전까지 다른 트랜잭션은 무조건적으로 기다려야 하는 상황이 생기다 보니 데드락을 피할 수가 없었다. 결국 다른 방법을 통해서 동시성이슈를 막아야 했다.
비관적 락? vs 낙관적 락? 그리고 자바의 synchronized 와 concurrentHashMap
이때 다른 방법의 동시성이슈 해결법에 대해 공부를 해보니 낙관적 락, 비관적 락에 대해서 알 수 있었다. 낙관적 락, 비관적 락에 대해서는 다른 분들이 설명을 더 잘해놓았기 때문에, 설명을 하진 않겠다.. 난 결국 자바에서 제공하는 synchronized lock과 concurrentHashMap으로 해결했다. 김영한 선생님의 강의에서 동시성 이슈에 대해서 계속 언급하셨던 concurrentHashMap인데 이걸 이런 이슈가 있기에 말씀을 해주셨구나...하고 이제서야 깨닫게 되었다.... 역시 사람은 자기가 당해봐야 아는것 같다. 그리고 프론트에서도 따닥을 막기 위해서 최초 1회 구매 버튼을 누른 이후엔 로딩스피너와 버튼을 disabled 처리를 해서 겨우 해결했다. 이렇게 나의 개발 커리어에서 잊지 못할 동시성이슈를 여기서 맞아버렸다.. 이때 느낀 점은 내가 짜는 코드 한 줄 한 줄이 매우 중요하다는 것을 느꼈다.
문제를 뒤집어서 생각해보자
뜬금없지만 주식투자의 귀재인 워렌버핏은 다들 알 것이다. 워렌버핏의 오른팔인 찰리멍거가 일에 대해서 인터뷰를 하는 영상을 접하게 됐는데 최근에 맞은 동시성 이슈에 대해서 충격이 컸던 터라 찰리멍거의 말이 너무 와닿아서 나도 개발할 때 접목을 시켜보려 하고 있다.
찰리멍거가 공군 장교로서 기상 예측 업무를 맡았을 때의 경험을 얘기해 줬다. 찰리멍거는 문제는 뒤집어서 생각하면 쉬워진다. 라고 했다. 내가 어떻게 해야 전투기 조종사에게 최상일지 고민하기보다, 내가 어떻게 해야 전투기 조종사를 죽일 수 있을지를 고민했다고 한다.
그리고 자신이 생각했을 때 세 가지를 안 지키면 조종사가 무조건 죽는다라고 결론을 냈고, 근무를 할 때 꼭 그 세 가지만큼은 지켰다고 한다.
이렇게 문제를 거꾸로 뒤집고, 어떤 상황을 피해야 할지 고민함으로써 핵심을 짚어낼 수 있었던 것 같다. 나도 이 대목을 접한 이후, 중요한 비즈니스 도메인을 개발할 때 좋은 상황을 생각하기보단, 반대로 안 좋은 상황을 우선적으로 생각해 보면서 개발을 하게 됐다.
'유저가 주문에 실패하려면 어떻게 해야 하지?'
'유저가 비정상적인 주문을 하려면 어떻게 해야 하지?'
'유저가 로그인에 실패하려면 어떻게 해야 하지?'
이렇게 하니까 비로소 내가 해야 할 일을 할 수 있게 됐다.
마무리 하며
동시성이슈를 맞고 내 자신이 한없이 부족하다는걸 깨닿고 요즘에 다시 안주하지않고 공부를 해야겠다라는 생각이 가장 컸다. 그래서 나름 올해의 목표를 잡아봤는데 우선 개발 서적 읽기를 목표로 했다. 회사에서도 점심시간에 짬짬이 책을 읽고 있고, 퇴근후 집에 와서 운동 끝나고 자기전에 책을 읽고 자곤 한다. 항상 뭐가 됐던 운동이던 책읽기던 맘먹기도 힘들뿐더러 꾸준히 하는게 정말 어렵다고 생각한다. 그래도 남들이 다 힘들어하고 어려워하는것을 꾸준히 하다보면 내 스스로가 많이 성장했을거라고 생각한다. 그래서 요즘에 클린 아키텍처, 도메인 주도 개발, CQRS에 관심이 많아 이 쪽의 책을 읽어보려고 한다. 그 이후에는 김창준님의 함께 자라기를 읽어볼 예정이다. 책을 다 읽고 책에 대한 느낀점? 독후감이라고 해도 될진 모르겠지만.. 독후감을 책을 다 읽고 몇자를 적어볼까 생각중이다. 다음 회고는 어떤 이슈와 어떤 이야기가 있을 지는 모르겠지만 올해도 많이 성장하는 한 해가 됐으면 좋겠다.
'개발 회고' 카테고리의 다른 글
나의 첫 해커톤 개발 회고록(24.06.24~26) (0) | 2024.06.26 |
---|