written by yechoi

[webserv] 3개월 동안 웹서버 만든 후기 본문

Born 2 Code/Web

[webserv] 3개월 동안 웹서버 만든 후기

yechoi 2021. 4. 30. 00:43
반응형

 

프로그래밍을 시작한지 어연 1년 6개월. 지금까지 한 것 중 가장 큰 프로젝트가 막을 내렸다. 3명이서 팀을 이뤄 c++로 nginx와 비슷하려고 노력한 웹서버를 하나 만들었다. 스터디를 시작한 것이 2월 1일이고 프로젝트를 제출한 것이 4월 24일이니까, 거의 꽉채운 세달이 걸렸다. 오랜 걸린 만큼이나 느끼고 배운 것들이 많다. 이번 프로젝트를 통해 성장한 점을 기록해보고자 한다. 

 

 

git으로 구조화된 협업하기

이전에도 git은 사용했지만, 거의 코드 저장소로 사용했을 뿐이다. 이번 프로젝트를 하면서 git을 좀더 '협업툴스럽게' 사용할 수 있게 됐다.

 

 

900여개의 커밋수

 

우리는 git으로 협업을 관리했고, 그 과정에서 처음부터 끝까지 지키려고 한 규칙들이 있다. 이러한 규칙들이 있어 비효율적인 소통을 줄일 수 있었다.

 

1. 커밋컨벤션 지키기

더보기

커밋 형식

[커밋 명령어] 메세지

커밋 명령어

FEAT: 파일을 생성할 때, 문서를 추가할 때, 코드를 추가할 때

  • create: 생성할 때
  • add : 추가할 때

FIX: 오류를 수정

REFACTOR: 오류는 아니지만 수정

DELETE: 파일/디렉토리 삭제 또는 코드의 일부분을 삭제할 때

 

2. 브랜치로 작업하고 PR로 마스터에 머지하기

마스터에 직접 병합하지 않고, 각자의 브랜치에서 작업을 하다가 PR을 날리면 팀원들의 리뷰를 거쳐 마스터에 머지한다. 브랜치에서 작업을 하기 때문에, 서로 독립적인 업무를 맡았다면 같은 시간 각자의 업무에 집중할 수 있다. PR을 날리는 순간에만 이전의 master를 확인하고 업데이트해, 충돌이 나지 않도록 신경써주면 된다. 

 

3. 코딩컨벤션 지키기 

더보기

Team Coding Convention

들여쓰기

  • Tab Size: 4

변수명

  • 클래스의 멤버 변수
    • prefix: m_
    • 소문자
    • 스네이크 표기 ('_')

함수

  • 소문자
  • 카멜 표기 (중간에 대문자 ex, void webServer(void);)
  • getter 함수
    • prefix: get_m_
    • 스네이크 표기 get_m_variable
    • 한 줄로 쓸 수 있는 경우 한줄로 표현

(하략)

각자 함수와 변수명을 짓는 방식이 다르다면...? 거기서 또다른 커뮤니케이션 비용이 발생할 것이다. 

 

3. 다뤄야 할 문제가 있으면 깃헙의 이슈와 칸반보드를 활용

 

우리가 팀프로젝트를 하며 남긴 100여개의 이슈
우리가 올렸던 이슈들의 일부

 

특히 이슈와 칸반보드로 소통한 것이 도움이 많이 됐다. 우리가 어떤 문제를 겪고 있는지 글로 풀어냈을 때 명확해지기도 하고, 이슈로 기록해두면 이것저것 여러 이슈를 처리하다가 잊어버릴 염려도 없다. 해결한 이후에도 해결한 커밋과 연동돼 기록이 남길 수 있으니, 나중에 돌이켜볼 때도 좋다. 이러한 방식을 제안해준 팀원에게 고맙다. 

 

 

나쁜 코딩 습관 바로잡기

페어 코딩을 할 때도 많다보니 코딩 습관을 포착할 기회도 생긴다. 나의 경우 설계를 완료한 부분까지 한번에 다 짜버리는 습관을 가지고 있었다. 이번 과제에서 맡았던 부분 중 하나인 request 파싱을 예로 들어보자.  

 

 

처음에 코드를 썼을 때, 나는 이 모든 메세지의 파싱 과정을 한번에 다 적어내려갔다. 리퀘스트 라인부터 바디까지의 파싱 그리고 그 안에서의 에러처리까지 모두 말이다. 그러고 돌려본 코드는 역시나 제대로 돌아가지 않았다. 이미 상당한 양의 코드를 썼기 때문에, 어느 부분에서 내 의도와 다르게 작동하는지 파악하기가 쉽지 않았다. 테스트 주기를 짧게 했더라면 문제를 찾는 시간을 훨씬 줄일 수 있다. 문제를 파악한 이후론 중간중간 확인해나가며 코드를 작성했다.

 

 

최적화하기 = C++ 딥다이브 하기

 

무관한 이미지 아님,,,

 

이전 과제들도 코드를 다듬는다고 다듬었지만, 이번 과제에 비하면 그건 다듬는 것도 아니었다. 또 이전 과제에선 리팩토링이 '선택사항'이었다면, 웹서브에서는 '필수사항'이었다.

이 과제를 통과하기 위해선 과제에서 제시한 테스터를 통과해야 한다. 메소드, 바디 길이, 요청수가 각기 다르게 요청이 들어오는데, 우리는 이 모든 테스트를 통과하기까지 처음엔 두시간이 넘는 시간이 걸렸다. 사실 테스터를 켜놓고 잠들었기 때문에 최소 두시간이라는 것이지, 걸린 시간이 네시간인지 다섯시간일지도 모른다.

평가 받는 시간 내에 테스터가 돌아가려면 리팩토링은 필수적이다. 성능 개선을 위해 C++에 대해 더 많은 공부를 해야했다. 복사를 하지 않고 데이터를 전달할 수 있는 방법, 소켓 버퍼 제한을 벗어나는 방법 등을 고민하면서 C++에 대해 딥다이브해볼 수 있는 시간이었다.

여담으로 가장 처음에 했던 리팩토링은 매개변수를 참조자로 받는 것이었다. 매개변수를 매번 복사하지 않게 되는 것만으로도 성능을 크게 개선할 수 있었는데, 참조자의 존재 의미를 피부에 와닿게 느꼈던 순간이다.

 

 

자연스럽게 따라오는 HTTP 지식

웹서버를 만들었으니 HTTP에 대한 지식은 무조건적으로 따라온다. 웹프로젝트를 하나 하면서 HTTP에 대해선 겉핥기하고 있었던 것이나 마찬가지였는데, 이번 기회로 아주 딥하게 탐구해볼 수 있었다. 직접 구현하려면 메소드, 메세지 형식, 헤더 필드와 값의 의미 등등 세부적인 내용을 모두 알고 있어야 한다. 

특히나 RFC를 읽고 구현하는 과정이 의미가 있었다. RFC를 굳이 다 읽지 않아도 된다는 입장도 있지만, 우리는 초기 스터디 단계에서 1주일 정도 할애해 RFC 7230부터 7235까지 모두 읽었다. 그 당시에는 RFC를 읽는다는 게 버겁고 한약먹는 것만 같았는데, 과제를 하다보면 이처럼 알짜배기도 없다. 

앞으로의 개발 여정에서도 지금 얻은 이 지식이 값지게 사용될 것이라는 걸 확신한다. 

 

 

아쉬운 점? 

시간이 많이 소요된 원인을 꼽아보자면, 타인의 코드를 너무 참고하지 않으려 했다는 점이다. 먼저 과제를 끝낸 카뎃들에게 여러모로 도움을 요청하고 받기도 했지만, 정작 코드는 많이 둘러보지 않았다. 모종의 이유로 다른 이들의 코드를 참고하는 것에 대한 거부감이 있었기 때문이다. 그러다가 결국 다른 코드를 적극적으로 참고한 게 후반부인 CGI 부분을 리팩토링 할 때다. 우리의 아이디어로 해결을 해보려고 몇주고 고민을 했는데 결국 좋은 해법이 나오지 않았다. '이쯤이면 다른 코드를 봐야한다' 싶어 다른 웹서버 코드를 봤는데, 정말 생각해보지 못한 아이디어로 이 문제를 해결했더랬다. 간단한 아이디어였지만 성능 개선은 엄청났다. 그렇게 문제를 해결하고 나서야 '아 남의 코드 좀 뜯어 볼걸...' 싶었다. 

이건 팀 전체의 잘못된 습관 중 하나였는데, '충분히 실험하지 않고 문제를 단정짓는' 버릇이 있었다. 일례로 GET / 요청을 수백번 보내는 경우에서 우리의 웹서버는 처리 속도가 느렸다. 우리는 이때 문제상황을 '여러개의 리퀘스트 메세지가 한번에 동시에 들어오는 상황'이라고 정의했다. 여러개가 동시에 들어오는데 우리는 하나씩 처리하니 느리다고 판단했던 것이다. 그래서 여러개가 동시에 들어오면 동시에 처리하도록 로직을 바꿨다. 바꿨더니 웬걸? 메세지는 한번에 하나씩만 들어왔다. 문제상황부터 잘못 정의한 것이다. 리퀘스트 메세지 들어오는 상황만 찍어보면 제대로 파악할 수 있는 문제였는데 말이다. 우리에게 이런 문제가 있다는 걸 알고나서는 개선하기로 이야기를 나눴지만, 꽤 비용이 큰 깨달음이었다. 물론 깨닫기 이전보다는 훨씬 나은 상황!!

또 다른 아쉬운 점은 테스터와 평가는 통과했지만, 여전히 성능 개선의 여지가 있다는 점? 정 아쉬우면 수정해보기로 하고, 쉬엄쉬엄 컨테이너 만들다가 마지막 과제로 또 달려가야겠다! (급마무리)

반응형