실험적인 프로젝트
이번 프로젝트를 시작할 때, 기존 프로젝트에 대해서 몇 가지 개선안을 적용하는 실험을 하기로 하였다.
1. 함수명을 길고 명확하게 짓기 : 기존 프로젝트는 깔끔한 네이밍을 많이 선호하였다. 하지만, 코드에서 가끔씩 너무 짧은 단어라 이해가 가지 않을 때가 있었다. '클린 코드'에서는 길더라도 깔끔하고 명확한 네이밍을 심사숙고해서 지으라 추천하였고 이를 적용하기로 하였다.
2. 함수를 최대한 작게 나누기(단일 책임 지향) : 클린 코드에서는 단일 책임을 추구하라하였고, 이 부분이 유지보수에 매우 유용하다 적혀있었다. 이 부분에서 공감되는 부분이 있었기에 적용하기로 하였다. 추가적으로, 신문을 읽듯이 추상화하여 작성하기도 목표에 있었다.
3. barrel 방식을 버리고, 모든 폴더의 대표 파일을 index.tsx로 짓기 : 프로젝트를 시작할 때쯤, barrel 방식이 런타임 전/후로 성능 손실을 낸다는 글을 보았다. 우리 본부는 barrel 컨벤션을 사용중이지만 파일수가 워낙 적기 때문에 성능에 큰 문제가 없었다.
하지만, 난 원래 barrel 방식이 폴더 구조를 더 복잡하게 만든다 생각하여서 별로 안 좋아했고, 성능문제가 있으면 쓸 필요가 없다 생각하여서 다른 방식을 써보기로 했다.
(barrel 방식에 대한 글 번역 : https://github.com/yeonjuan/dev-blog/blob/master/JavaScript/speeding-up-the-javascript-ecosystem-the-barrel-file-debacle.md)
4. 문서화 : '버스지수' 라는 단어를 알게 되고, 모든 프로젝트에 대해서 반성했다.
문서화를 하여서 프로젝트의 중요한 부분을 잘 정리해두기로 했다. 문서를 관리/운영하는 것은 다음 목표로 하겠다.
(이 부분은 2장에서 상세하게 다룰 것이다.)
실험의 결과는...

1. 함수명을 길고 명확하게 짓기
다시 확인했을 때도, 이 함수가 무엇을 하는 지 쉽게 알 수 있어서 편하고 좋았다.
[장점1. 직관적이다.]
예전에 getShapes()라 함수명을 지었다면, 지금은getCurrentPageShapeList() 로 지었다.
getShapes()는 "shape들을 가져온다" 라 생각할 것이다.
하지만, getCurrentPageShapeList()는 "(현재 페이지의) shape들을 (list 형식으로) 가져온다"로 훨씬 명확하게 생각하게 된다.
이름이 길지만 굉장히 직관적이기 때문에, 함수의 역할(책임)에 대해서 고민할 필요가 없다. 그래서 나중에 에러가 발생하였을 때, 에러의 상황과 연관된 이름을 찾아서 빠르게 살펴볼 수 있어서 좋았다.
[장점2. 코드리뷰하기 좋다.]
직관적이다의 연장선이다.
아래처럼 적혀있으면 매우 읽기 편하지 않은가??
moveNextPage(){
const currentPage = findCurrentPage() // 현재 페이지를 찾음
isMaxPage(currentPage) // 다음 페이지가 있는 지 체크. (최대 페이지가 아닌 지 체크)
movePage(currentPage + 1) // 다음 페이지로 이동
}
큰 함수의 이름(책임)을 이해했다면, 큰 함수를 이루는 작은 단위의 함수들의 이름으로 동작을 이해하고, 작은 함수들을 리뷰하는 식으로 들어가면 된다. 주석이 없어도 리뷰를 하기 굉장히 편해진다.
사실, 이 부분은 단일 책임의 역할이 더 크지만, 결국 이름을 잘 지었기 때문에 직관적으로 이해할 수 있다는 생각이 들어서 여기에 추가하였다.
[단점 1. 단일 책임이 있을 때, 굉장히 잘 어울린다.]
만약, 함수가 단일 책임을 지향하지 않는다면 별로일 수도 있다.
activeToolbarMenuAndsetCanvasMode() 라는 함수가 있었다.
toolbar의 버튼을 누르면, 버튼이 활성화되면서 캔버스의 모드가 바뀌는 함수이다. 클린 코드에서 책임이 여러 개면 모두 서술하라는 의견을 듣고 만들었지만.... 너무 길고 직관적으로 이해되지 않아서 위 장점들이 살아나기 힘들었다. (저자는 불가피하게 단일 책임이 안되는 경우 이렇게라도 지으라 하긴 했다.)
결국, activeTool() 이라는 함수로 바뀌었다.
[특이사항 1. 너무나 자율적이다.]
이번 프로젝트는 최초에는 나 혼자 프론트엔드를 개발하다가 나중에 사람들이 추가 투입된 거라 세세한 부분까지 이야기를 나누지 못했다.
그러다보니, 관점에 따라서 조금씩 다른 단어를 선택하는 일이 종종 있었다.
- 변수명이 생략가능하거나 두 가지 뜻 다 맞을 때 = whiteboard / board
- 신규 값이 생겨서, 배열이 업데이트 되는 경우 = create / add / push / update
- 같은 의미의 단어를 다르게 쓸 때 = check / confirm
변수명의 경우 백엔드는 'board'라는 변수명을 사용하고, 프론트는 'whiteboard'를 사용하고 있었는데, 이를 뒤늦게 알아챘다. 나중에 회고 때, 이 부분 때문에 많이 혼란스러웠다는 팀원들의 말을 듣고 변수명은 보일 경우 바로바로 통일해야겠다는 생각이 되었다.
추가로, 그러면 'whiteboard'로 어떻게 알아서 잘 사용했냐는 질문을 드렸는데, "파일명과 타입, 문서 모두 'whiteboard'로 통일되어 있어서 알아서 프론트 내부적으로는 그 변수를 선택하게 되었다"는 답변을 받았다. 타입을 잘 선언해두는 것이 이런 작은 부분에 많은 영향을 줄 수 있다는 생각이 하게되었습니다.
이 두 가지 케이스는 큰 문제를 발생시킨 것은 아니였지만, 상대적인 아쉬움이 있었습니다. 길고 명확하게 표현하기에 "함수명 길게 짓기"의 장점이 상쇄되는 것은 아니지만 통일하면 더 직관적이고 이해하기 쉬운 이름을 지을 수 있다는 생각을 하였습니다. 하지만, 서로 코드를 지속적으로 리뷰하면서 맞춰가면 팀과 프로젝트에 맞는 통일되고 멋진 이름이 나올 수 있다 생각해서 단점도 장점도 아닌 특이사항으로 적게되었습니다. (아직 첫 번째 프로젝트이기에... 아직 더 테스트할 부분이 있는 부분)
2. 함수를 최대한 작게 나누기(단일 책임 지향)

진짜 더 표현할 방법이 없을 정도로 좋았다.
그리고 미리 말해두지만, 단일 책임 지향이지 단일 책임은 아니였다.
[장점1. 유지보수의 매우 강력한 강점]
코드 짤 때는 "흠... 이렇게까지 해야할까?" 라는 생각을 많이했었다.
하지만, 나중에 버그를 찾을 때, 차이가 너무 명확했다. 버그가 생길 수 있는 함수가 너무 명확히 보였다. 왜냐하면, 책임이 다 나누어져있기 때문에 이 버그가 생길 수 있는 부분은 딱 여기였다. 그리고 대부분 거기가 맞다.
예전에 이런 버그가 생기면, 그냥 눈 앞이 깜깜했다. 실행한 곳부터 영향을 줄 수 있는 부분을 계속 역으로 추적하다가 "아하 여기구나" 하면서 간신히 찾고는 했다.
하지만, 지금은 "아 여기서 안되네? 아 이 케이스는 고려 못했네"가 끝이다.
이 시간이 진짜 10배 이상 차이나는 거 같다. 그렇다고 트래킹해야하는 버그가 안 생긴다는 말은 아니다.
버그의 원인을 찾는 시간을 많이 아낄 수 있으니, 그만큼 해결에 온전한 정신력과 체력으로 집중할 수 있으며, 가끔 트래킹해야하는 버그가 생겨도 다른 데서 많이 아껴두어서 마음이 편안한 상태이다.
[장점2. 매우 유익한 코드리뷰]
작은 책임은 결국 작게 추상화를 매우 잘했다는 소리가 된다.
moveNextPage(){
const currentPage = findCurrentPage() // 현재 페이지를 찾음
isMaxPage(currentPage) // 다음 페이지가 있는 지 체크. (최대 페이지가 아닌 지 체크)
movePage(currentPage + 1) // 다음 페이지로 이동
}
만약, 이 부분이 전부 추상화되지 않은 코드로 되어있다 생각해보자. (20줄 정도로 나열되어 있을 것이다.)
이 줄글 코드를 리뷰하다 보면, 아쉬운 부분이 생긴다.
- 작을지라도 이미 이전에 리뷰한 중복된 부분이 있을 수도 있을 수도 있다. (추상화되어 있지 않기에, 이를 읽고나서 파악하며, 같은 함수가 아니기에 유틸 함수를 찾기 전까지 완벽히 같은 지는 알 수 없다.)
- 리뷰 중인 줄(또는 단락)의 목적을 파악하기 쉽지 않다. (추상화가 되어 있지 않기에 정확한 용도는 직접 체크하면서 이해해야한다.)
- 리뷰한 단락이 어디서 어떤 식으로 사용되는 지 전체적인 흐름을 파악하기 쉽지 않다. (코드가 수정되면서, 더 이상 사용되지 않는 불필요한 부분이 생길 수도 있지만, 큰 흐름 속에서 놓칠 수도 있다.)
근데, 이를 하나의 책임을 가진 작은 함수로 추상화를 한다면 위 내용처럼 단 3줄이다.
짧은 함수로 추상화가 잘 되어있기 때문에, 흐름을 파악하는 것이 쉽고 중복된 부분을 파악하기 쉽다.
그리고 이 함수는 하나의 책임을 하는 짧은 코드들로 이루어져있어서, 목적이 명확하며 리뷰하기 쉬워진다. (왜냐면, 단 하나의 작은 기능만을 위한 함수이니까!)
그런 코드리뷰가 굉장히 짧아지며, 신규 작성된 기능에 대해서 꼼꼼하게 하기 좋아진다. 또한, 책임이 단순하니(목적이 명확하니) 이 코드의 동작원리에 대해서 잘 이해하게 된다!
[단점1. 단일 책임은 매우 어려워...]
사실 단일 책임을 지향이라 표현한 이유는 일부 포기할 수 밖에 없었던 부분이 있기 때문이다.
너무 심하게 단일 책임을 지향하다보면 너무 잘게 쪼개지면서 오히려 비효율적일 때가 생긴다.
벽돌까지만 나누어도 되는 부분을 모래알갱이로 분해하려는 느낌이다. 그래서, 이런 모래 알갱이들을 하나하나 체크해서 관리하는 게 더 비효율적일 때도 있다.
또한, 실무에서는 반드시 단일 책임을 할 수 없을 때도 있다. 이런 케이스에 억지로 단일 책임을 고집해봤자 답이 없는 문제에 미련하게 끙끙댈 뿐이다.
그렇지만, 위에 말한 엄청난 장점을 절대 포기할 수 없기에 난 책임의 최소화를 지향하는 방향으로 간 것이다.
이번 프로젝트에서는 2개 정도가 최대였던 걸로 기억하며 이 때문에 장점만 취할 수 있었다고 생각한다. (switch 문 제외)
[특이사항 1. 좋은 추상화는 시간을 필요로 한다.]
사람마다 다를 수 있지만, 나는 정말 리팩토링을 조금씩 많이 했다.
최초에는 코드나 추상화에서 부족한 점이 많았다. 그걸 조금씩 더 나은 방식으로 성장시킨 것이다.
아직 숙련도가 낮아서일 수 있지만, 결국 처음부터 좋은 코드는 짤 수 없었다.
이 과정 자체가 내 실력을 키워주는 좋은 기회이지만, 시간을 필요로 한다는 점에서 살짝 마음에 걸린다. 이를 위해서 많은 연습이 필요할 것 같다.
3. barrel 방식을 버리고, 모든 폴더의 대표 파일을 index.tsx로 짓기
망했다. 두 번 다시는 쓰고 싶지 않다는 후기를 공유받았다.
이유는 vscode와 MR review를 진행하였을 때, index.tsx만 7-8개 되어서 너무 헷갈린다는 점이었다...

공감되서 할 말이 없었다.
next는 페이지에만 적용하니까 그럴 수 있지만, 모든 컴포넌트와 폴더 구조의 적용하니 정말 별로였다. 아마 다음 프로젝트에서 부터는 index.tsx는 부분적용하고 그냥 전부 다 파일이름으로 쓰는 컨벤션을 사용하지 않을까 싶다..
결론 및 정리
문서화는 좀 할 말이 많을 것 같고 클린 코드 내용보다는 다른 책에서 배운 내용이다보니, 글을 나누어서 작성하려한다.
위 글에서 느낄 수 있었겠지만, 클린 코드를 추구하면서 느낀 점은 정말 너무 좋았다. 배우는 점도 너무 많았고, 이를 통해서 얻는 이점도 너무 많았다.
하지만, 익숙하지 않고 생각할 부분이 많다보니 다시 예전에 편한 방식으로 코드를 작성하고 싶어해서 이 부분을 계속 노력해야할 거 같다.
마지막으로, 클린 코드에서 테스트 코드를 굉장히 강조한다.
원래 이번 목표에 테스트 코드도 목표에 있었지만 다른 목표들에서 많은 시간을 소요하였고, 프론트 테스트 관련 서적을 좀 더 읽어보고 적용하고 싶어서 이번에는 적용하지 못했다.
다음 목표는 테스트코드를 적용하면서 얻은 장/단점을 서술해보고 싶다.
'개발 일기 > 회고' 카테고리의 다른 글
| [화이트보드 동시편집 프로젝트] 4. 마무리 (0) | 2024.05.06 |
|---|---|
| [화이트보드 동시편집 프로젝트] 3. 파트장 역할 수행기 (feat. 소통) (2) | 2024.04.29 |
| [화이트보드 동시편집 프로젝트] 2. 문서화 (0) | 2024.04.28 |
| [화이트보드 동시편집 프로젝트] 0. 회고 시작 (1) | 2024.04.14 |