코딩 스타일과 함수에 관한 생각

ko-ing·2021년 12월 11일
0

개발

목록 보기
2/2

회사를 다니면서 코드 스타일에도 개인취향이 확실하다는 것을 실감하는 것 같다. 어떤 취향이든 코드를 아예 읽을 수 없을 정도만 아니라면 존중하는 것이 마땅하다고 생각하고, 코드의 통일성은 리뷰하고 토론하며 팀으로서 오랜 기간에 걸쳐 맞춰가는 것이라고 생각한다.

최근에 코드 스타일이나 컨벤션에 관한 이야기를 할 기회가 많았는데, 이 기회에 나는 어떤 코드 스타일을 추구하고 싶은지 정리해보려고 한다. 이번 포스팅에서는 함수에 관한 이야기를 하려고 한다.

함수 세로 길이는 한 화면을 넘어가지 않게 하자

나는 가독성을 위해 한 함수의 세로 길이가 한 화면을 넘어가면 나쁘다고 생각한다.

레거시를 만들지 않는 방법은, 읽기 쉽고 처음봐도 습득하기 쉽도록 코드를 작성하는 것이라고 생각한다. 그래서 한 단위의 코드는 한 눈에 들어오는 것이 좋다. 로직을 따라가며 코드를 읽을 때, 함수 단위로 보는 경우가 많기 때문에, 함수의 세로 길이가 길지 않은 것이 매우 중요하다고 생각한다.

함수 세로 길이가 매우 길어 스크롤을 3~4번 씩 내려야 함수 끝에 도달한다고 생각해보자. 함수 선언부 주변 코드를 기억하고 끝 부분의 코드의 로직을 살펴봐야할 경우 왕복 6~8번의 스크롤을 오가며 코드 구조를 기억해야하는데, 비동기 처리 등 로직이 조금만이라도 복잡해지거나 직관적이지 않은 코드일 경우 기억해두기 매우 어렵다.

그래서 함수의 세로 길이는 한 화면을 넘어가면 안 된다고 생각한다.

함수의 가로 길이는 한 화면을 조금 넘어가는 수준이라도 괜찮다고 생각한다. 단, 해당 코드가 변수 할당이나 함수 호출 등일 경우 네이밍을 잘 해서 가로로 긴 코드를 직관적으로 이해할 수 있게 하는 선에서 작성해야한다고 생각한다.

다른 문맥이라면 함수를 나누자

간혹 코딩을 하다보면, 절차지향적이고 굉장히 많은 일들을 하는 하나의 함수를 나도 모르게 작성할 때가 있다. 그럴 때는 다시 함수를 읽으면서 문맥에 따라 함수를 나누는 습관을 가지려고 노력하고 있다.

예를 들어, 한 함수에 유저 엔티티의 상태를 변경하고 저장하는 로직과 유저에게 알림을 보내는 로직, 인자를 검사하는 로직 등이 섞여있다고 하자. 이럴 경우, updateUser(), alertUser(), checkParams() 등의 함수로 나누어 두는 것이 가독성에 훨씬 좋다고 생각한다.

물론 위의 예시 같이 완벽하게 나눌 수 없을 때가 있는데, 이런 경우는 엔티티 설계나 함수 설계 자체를 잘못 하여 그런 경우가 많았던 것 같다. 그런 경우, 엔티티나 함수를 다시 설계하는 것이 바람직하지만, 리소스가 부족한 경우에는 그럴 수가 없다. 그렇다면 다음에 리팩토링을 하기 쉬운 정도까지만이라도 함수를 나누는 것이 좋다고 생각한다.

함수를 나누는 이유는 크게 가독성과 재사용성 두 가지라고 생각한다. 재사용성을 더 중요시하는 사람들 중 private 함수를 많이 만드는 것을 추천하지 않는 사람들도 있다. 이것은 취향의 영역이라고 생각하지만, 나는 private 함수로 나누는 것이 함수 가독성을 크게 높히기 때문에 큰 문맥에서 private 함수로 함수를 쪼개는 것은 좋은 접근법이라고 생각한다. 물론 좋은 네이밍이 동반된다는 가정 하의 일이긴 하다.

함수 네이밍은 적당히 자세히?

함수 네이밍은 항상 고민거리인 것 같다. 네이밍을 최대한 자세히 할 것이냐, 함축적으로 할 것이냐에 대한 물음에 대부분은 조금 더 길더라도 자세한 게 좋다고 이야기를 하는 것 같다. 클린 코드나, 이펙티브 자바에서도 네이밍을 자세히 하자고 했던 것 같다. (아닐 수도)

어쨌든, 그렇다면 함수 네이밍은 무조건 자세한 것이 좋을까? 만약 한 함수가 하는 일이 많아서 함수 이름의 길이가 매우 길어진다면?
위에서 예시로 들었던 updateUser(), alertUser(), checkParams() 함수를 담고 있는 상위 함수의 이름은 어떻게 되어야할까? 이 함수의 목적이 유저 상태를 휴면으로 만드는 함수라면, inactivateUser()가 어떨까.
여기서 한 가지 우려되는 점은, inactivateUser만으로는 이 함수가 유저에게 alert를 보낸다는 것을 알 수가 없다는 것이다. 이때 우리는 inactivateUserAndAlert()라는 네이밍도 생각해볼 수 있는데, 이 경우에는 다른 기능이 추가될 경우 inactivateUserAndAlertAndXXXAndXXX라는 매우 긴 함수 이름을 가질 수도 있다.
이런 경우 우리가 함수 이름만 보고 판단했을 때 예상치 못한 side effect가 생길 수 있기 때문에 웬만하면 자세한 네이밍이 좋다고 생각한다. 하지만 함수 이름이 너무 길어지는 것을 방지하기 위해 큰 맥락을 가진 요소만 네이밍에 포함해야 하고, checkParams 같이 암묵적으로 예상할 수 있는 요소나 큰 side effect가 없는 요소는 굳이 포함할 필요는 없다고 생각한다.

이 문제는 전체 시스템의 레이어를 어떻게, 얼마나 깊게 가져갈 것인가와도 조금은 맞닿아있다고 생각한다. 보통 어플리케이션을 Controller-Service-Repository 세 레벨로 가져간다고 했을 때, 서비스 레이어의 크기가 얼마나 크냐에 따라 예상치 못한 side effect가 생길 가능성이 크기 때문이다. 그럴 때는 조금 더 면밀히 side effect를 살펴볼 필요가 있을 것 같다.

네이밍에 있어서 가장 중요한 것은 남들이 한번에 어떤 함수나 변수인지 알아볼 수 있느냐이다. 나만 아는 코드는 협업에 있어 매우 비능률적이며, 결국 나중에 다시 돌아와 코드를 읽었을 때 나 역시도 처음보는 코드처럼 느껴질 때가 있기 때문에 생산성을 올리는 것과 병목을 방지하는 것에 매우 중요한 요소라고 생각한다.


함수에 관한 생각은 이 정도이고, 앞으로 추가할 것이 있으면 추가할 생각이다.

profile
개발을 합시다!

0개의 댓글