서론
사실 이 포스팅을 쓰게 된 것에는 깊은 사연이 있다... 얼마 전 지인의 팀플 코드를 같이 디버깅해본 적이 있다. 이때 나는 약간 충격을 받았다. 코드가 어려워서? 그것도 조금은 있겠지만.... 제일 큰 이유는 한개의 소스 파일에 1200줄 이상 들어있는 코드와...기능이 각기 다른 수십 개의 함수가 main.c라는 한 개의 파일에 뒤섞여서 코드의 흐름을 알아볼 수 없었기 때문이다.
특히나 나는 그 코드를 같이 짠 것도 아니고 처음 읽는 입장이니까 더욱 코드를 파악하기 어려웠다. 코드 파악에만 몇 시간이 걸렸다.
사실 예전의 나는 오히려 그것보다 훨씬 코드를 정말 어지럽게 짜고 함수를 나누지 않았기 때문에... 내가 절대 뭐라할 수 없는 일인 것 안다. 그렇지만 코드를 깔끔하게 짜는 법은 절대 학교에서 누군가 가르쳐주지 않더라...
따라서 내 소박한 지식을 나눠보고자 포스팅을 쓴다.
아직도 난 많이 부족하겠지만, 그래도 내가 거쳐온 과정을 통해 최소한 코드를 분류하고, 정리하고, 파일과 함수를 나누는 법은 잘 숙지하게 되었다고 생각한다. (실제로 내가 활동 중인 42서울 코드 규정에는 함수 개당 길이 제한, 변수 개수 제한 등등 까지 있다)
더보기
(요즘 나는 42서울을 통해 내 코드를 타인에게 코드리뷰 받고, 타인의 몇 백줄 되는 코드들을 읽으며 코드리뷰 해주는 일상을 반복해오고 있다. 또한 요즘은, 스터디에서 페어프로그래밍을 통한 자료구조 구현을 하며 며칠에 한번씩 500줄 넘는 코드를 짠다. 그래서 코드를 깔끔하게 짜는 것을 1순위로 두고 코드를 짜고 있다. 남에게 내 코드에 대한 코드 리뷰를 받을 때, 코드 읽기 어렵다는 쿠사리를 먹어가며 고쳐온 결과다... 다행히 전보다는 훨씬 나아졌다.)
코드 문제점 파악
해당 과제는, 한 txt 파일을 읽어와서 그 안의 데이터베이스를 검색-삭제-추가하는 그런 프로젝트였다. 여기에서 모든 기능을 각각 배열과 연결리스트로 둘다 구현해야하는 그런 것이었다.
일단 문제상황이 어떤 느낌이었는 지 대략 의사코드로 함수를 써보겠다..
#include <헤더>
#include ...
..
배열_검색_3()
링크드리스트_데이터추가2()
링크드리스트_출력()
배열에_데이터 추가2()
배열_검색_2()
배열_데이터추가1()
링크드리스트_검색2()
링크드리스트 어쩌고3()
배열검색어쩌고1()
링크드리스트 어쩌고2()
배열_출력()
.... 등등 수십개의 함수
int main()
{
... (100줄이 훨씬 넘는 메인문)
return (0)
}
이 코드의 문제점은 대략 이렇다.
- 배열과 관련된 함수들은 그들끼리, 연결리스트 관련 함수는 그것들끼리 모여있어야 보기 편한데, 여기엔 배열이나 연결리스트 관련 함수가 순서 상관없이 마구 뒤섞여있다.
- 배열_검색() 관련 함수는 검색 함수1, 2, 3끼리 위아래 붙어서 모여있어야 보기 편한데, 관련 함수들이 위 아래 중간 할 것 없이 수십개의 함수 사이에 숨어서 무차별적으로 분포한다.
- 배열을 검색하는 기능을 가진 함수들을 차례차례 찾아서 살펴보는 것 자체가 미로찾기 수준이다. 하나를 찾으면 다음 함수를 찾으려고 코드를 샅샅이 뒤지는 과정을 반복한다.
- 이때 코드 흐름을 놓치기 쉽다. 그러면 처음부터 다시 읽기 시작해야한다....(오마이갓)
- 배열을 검색하는 기능을 가진 함수들을 차례차례 찾아서 살펴보는 것 자체가 미로찾기 수준이다. 하나를 찾으면 다음 함수를 찾으려고 코드를 샅샅이 뒤지는 과정을 반복한다.
- 일단 main이 너무 밑바닥에 존재해서 너무 읽기가 힘들다. 게다가 설사가상으로 메인 함수까지 굉장히 길었다.
- main 함수조차 너무 길고, 흐름이 분류되있지 않아서 파악이 힘들었다.
이러면 어떤 문제가 생기는 가?
- 코드를 짠 사람이 아니라면 그 누구라도 코드를 파악하기 힘들다.
- 코드에 문제가 생겼을 때 어느 부분인지 찾는 게 힘들다.
- 심지어 문제가 난 부분을 알더라도 위쪽으로 한참 이동해서 해당 함수를 살피다가~ 아래쪽 끝까지 메인으로 내려와 메인을 고쳐 다시 실행해보고~ 이러는 데 불필요한 시간이 엄청나게 소요된다.
- 메인문을 봐도 코드의 흐름 자체가 안보인다.
좋은 프로그래머의 요건
실제 프로그래머는 혼자서 일을 하지 않는다. 사람이기 때문이다. 작은 프로젝트 정도는 혼자 할 수 있겠다. 그런데 회사에서 그리고 사회에서 요구하는 건 큰 프로젝트다. 그러니 필연적으로 누군가와 일을 분담해서 하게 될 수 밖에 없다. 심지어 프리랜서라고 해도 그 일감은 회사나 업체의 큰 일을 나눠받아서 하는 것이다.
이때 코드를 남이 파악하기 어렵게 짜버린다면 어떻게 되겠는가. 당신의 코드를 받은 뒤 프로젝트의 나머지 부분을 이어서 만들어야하는 동료가 많이 곤란해진다.
코드를 어렵게 짠다는 것은 결코 바람직하지 않다. 실력이 좋은 프로그래머는 이해하기 쉬운 깔끔하고 간단한 코드를 짠다. 항상 남이 읽을 경우를 염두에 두고 코드를 깔끔하게 만드는 연습을 해야한다.
이 문제 상황을 통해 어떻게 코드를 구성하면 보기 좋고 깔끔한 지 구상해 볼 수 있다.
그 방법은 다음과 같다.
코드 깔끔하게 구성하는 법
위의 문제상황에 있는 문제점들을 정확히 반대로 뒤짚으면 그 해결책이 된다.
바닥에 쌓여있는 책들을 도서관 책장에 정리한다고 생각하며 이 방법들을 읽어보자.
- 배열-연결리스트 관련 함수들이 순서 상관 없이 마구 뒤섞여 있다.
- 해결책 : 배열관련 함수는 배열 관련 함수끼리, 연결리스트 관련 함수는 연결리스트 관련 함수끼리 순서를 묶어서 정리한다.
- 배열_검색() 함수들이 코드 여기저기 멀찍멀찍 아무데나 분포한다.
- 해결책: 비슷한/같은 기능을 하는 함수들끼리 위아래로 붙어있게끔 모아서 정리한다.
- 메인 문이 너무 길다 :
- 해결책 : main을 깔끔하게 짜려면 다음 방법을 써야한다. 흐름을 명시적으로 나타내는 함수명을 가진 함수들로 기능을 나눠 코드를 나눠담는다. main의 코드 줄 수는 무조건 짧게 유지하는게 좋다. 무조건이다.
함수는 너무 길지 않게 만드는 것이 바람직하다. 안된다면 그 안의 자잘한 기능들/조건문 같은 것들을 또다른 작은 함수로 빼서 최적화시켜라.
파일 나누기
자 이제 코드를 분류 별로 기능별로 순서를 묶어 정리했다고 해보자. 그런데 아직도 한 파일에 코드가 너~~~~무 많이 들어있다...
만약 회사일으로 프로그래밍을 한다고 가정해보자.
당신은 몇 천줄 몇 만줄의 소스코드를 짯다. 그리고 한개의 소스 파일에 그 코드를 전부 넣어두었다. 그런데 이 몇 만줄이나 되는 코드에서 문제가 난다. 그러면 심지어 간단한 문제라 할지라도, 문제를 찾기가 정말정말 오래 걸릴 것이다.
코드를 위로 갔다 아래로 갔다 메인과 위쪽 함수들을 번갈아 위아래로 쉐킷쉐킷하며 읽는다. 그러면 간단한 문제 하나 찾지 못하고, 코드와 함수간에 스크롤로 이동하는데만 99%의 시간을 뺏기게 될 것이다.
"그러면 나더러 어떻게 하란거에요. 뭐 다른 방법이 있나요?"
있다!
바로 파일을 여러 개로 나누는 것이다.
저 팀프로젝트 코드의 경우에, 연결리스트 관련 함수는 use_Linkedlist.c, 배열 관련 함수는 use_Array.c 라는 파일에 나눠담는다고 해보자. 그리고 그 이외의 함수와 메인문은 main.c에 담는다.
이런 경우, 함수를 파악하기 엄청 수월해진다.
"파일을 여러 개로 나눠요? 그러면 main.c 파일에서 어떻게 다른 파일의 함수를 읽어오나요? 게다가 컴파일은 어떻게 해요?"
바로 사용자 헤더파일을 만들어 모든 파일 상단에 선언해놓는 것이다. 그리고 그 여러개의 파일들을 함꺼번에 컴파일하려면 명령어를 사용하거나, makefile을 사용하면 된다.
이대로는 너무 포스팅이 길어질 것 같으니 다음 편에 이어서 파일 나누기 (헤더파일 만들기, Makefile이 무엇인지)를 이어서 설명하도록 하겠다.
'IT > 왕초보 강의' 카테고리의 다른 글
[짱 중요한 팁] 소스 파일 하나에 몇 백, 몇 천줄의 코드를 몰아넣는 그대에게 - 2편 (feat. 헤더파일) (0) | 2021.12.19 |
---|---|
[Github] 깃허브 왕초보 사용법 - 기본편3 (0) | 2021.12.19 |
[Github] 깃허브 왕초보 사용법 - 기본편2 (0) | 2021.12.19 |
[Github] 깃허브 왕초보 사용법 - 기본편1 (1) | 2021.12.19 |