프로그래밍을 잘하는 5가지 방법 — 1. 자료구조와 알고리즘 공부하기

1. 자료구조와 알고리즘 공부하기
2. 가독성 신경쓰기
3. 공식 문서 및 메뉴얼 꼭 확인하기
4. 편집증적인 검증과 자동화
5. 똑똑한 사람들 따라하기


지난 6월 ‘자료구조와 알고리즘’에 대한 의견을 작성한 적이 있었고, 그와 함께 프로그래밍을 잘하는 방법에 대한 문의를 지속적으로 받아왔다. 나도 수준 높은 프로그래머는 아니라서 설득력이 있을지는 잘 모르겠지만, 최소한 그 동안의 멘토링 노하우를 바탕으로 노비스(Novice)가 좋은 주니어(Junior)가 되기 위해 갖추어야 할 자세에 대해서는 자신있게 작성할 수 있을 것 같아 글을 적는다.

앞으로 작성할 내용들은 특정 기술 등에 대한 것이 아니라 좀 더 형이상학적인 생각하는 방향과 자세에 대한 부분들에 대해 언급하려고 한다. 지금과 같이 기술이 급변하는 시대일수록 기본이 중요하다고 본다.

상기 순서는 중요성과 종속성에 바탕을 두고 정하였다.

자료구조와 알고리즘

우리는 인터넷에서 아직도 ‘자료구조와 알고리즘’이 중요한가에 대해서 해묵은 논쟁을 벌이고 있다. 나는 이 문제가 왜 논쟁의 여지가 없는지에 대해 프로그래밍적인 측면에서만 이야기 하겠다.

우리가 프로그래밍을 처음 접하게 되면, C언어 기준으로 아래와 같은 코드를 처음 접하게 된다.

#include <stdio.h>
int main(int argc, char ** argv) {
 printf(“Hello, World!”);
 return 0;
}

일반적으로는 기본 문법을 설명하기 위해 많이 사용하는 코드지만, 사실 저 곳에는 프로그래밍의 근본적인 내용이 대부분 포함되어 있다.


시스템과 서비스에서의 데이터와 로직

컴퓨터가 하는 일은 데이터를 읽어서 처리한 후 다시 저장하는 것이다. CPU는 레지스트리와 메모리의 값을 읽어서 처리한 후 다시 레지스트리와 메모리에 돌려 보낸다. 메모리는 데이터를 하드디스크 등과 같은 I/O 디바이스에서 복사한다. 이 때, 어떤 데이터를 어느 주소에 저장할지 판단하고 결정하는 과정을 거치게 된다. 그리고 하드디스크에는 파일시스템이라는 것이 있다. 파일시스템에서는 정보가 어떻게 저장되고 업데이트 되고 삭제되었는지에 대한 실제 데이터와 메타데이터가 파편화 되어 존재하며, 외부 요청에 따라 필요한 데이터를 조합해서 응답으로 돌려준다.

좀 더 멀리 가보자. 우리가 웹 어플리케이션을 만든다고 하면, 데이터는 데이터베이스에 저장되고, Spring이나 Django 같은 웹프레임워크들은 이 데이터를 읽어서 사용자의 브라우저에 페이지를 보여주거나 RESTful API의 결과로 돌려주게 된다. 검색 시스템이나 메세지 큐 시스템 같은 특정 목적의 시스템들 역시 데이터를 저장하고 가공하고 읽어들이는 행동을 벗어나지 못한다.

마이크로 서비스 아키텍처의 일도 다르지 않다. 데이터를 포함한 요청을 받고, 그 데이터를 가공해서 다른 서비스들에 데이터와 함께 요청을 보낸다. 다른 서비스들이 응답으로 보낸 데이터를 받아서 가공한 뒤 내가 받은 요청에 적절한 데이터를 응답으로 보낸다.


코드에서의 데이터와 로직

처음에는 너무 미시적인 세계를 이야기 했고, 그 다음은 너무 큰 서비스를 말했으니 이번에는 코드 레벨로 돌아와보자.

먼저, 구문을 기준으로 보자. 우리는 먼저 변수를 선언하고, 변수에 값을 넣는다. 어떨 때는 상수를 선언하기도 한다. 그리고 if 또는 for 문을 통하여 변수의 값을 변경하여 다른 변수에 저장하거나 원래의 변수값을 변경한다.

함수 레벨에서 추가되는 것은 입력 변수가 있다는 것과 특정한 값을 리턴(return)해야 한다는 것 뿐이다. OOP에서 객체는 데이터를 멤버변수라는 개념으로 유지할 수 있고, 멤버함수는 입력변수와 구문에 선언한 변수 이외에 멤버변수도 접근하고 가공할 수 있다.

우리가 코드를 작성할 때, 고려해야할 것들은 너무나도 많지만 그 중에서도 가장 먼저 해야 할 일은 입력 데이터의 형태를 파악하고 출력(저장/리턴) 데이터의 형태를 파악하거나 결정해야 한다. 그리고 그 다음으로 입력→출력 사이에 어떤 가공(로직)이 필요한지를 결정한다. 마지막으로 로직을 수행하기에 가장 적합한 데이터 구조가 어떤 것인지 결정해야 한다. 그리고 이렇게 파악되거나 결정된 내용을 바탕으로 실제 구현을 하게 되는 것이다.


그리고 우리가 말하는 프로그래밍이라는 것은 이런 활동이 프랙탈(fractal)처럼 확장되어 가는 것이다.

Data and Logic Fractal

‘자료구조와 알고리즘’ 논쟁을 보면 중요하지 않다는 측의 주장 중 가장 많은 논거가 “이미 대부분의 자료구조와 알고리즘은 라이브러리로 구현이 다 되어 있으니 잘 쓰기만 하면 된다.”라는 것이다. 하지만, 개인적으로 PS(Problem Solving) 트레이닝을 하면서 가장 많이 생각하는 것은 O(n) — 시간 복잡도(Time Complexity)와 공간 복잡도(Space Complexity) — 에 대한 것이다. 아무리 이미 주요 자료구조와 알고리즘이 라이브러리로 존재한다고 하더라도 내가 작성한 for문이 사용자의 HTTP 요청에 1시간만에 응답할 수 있다거나 1GB의데이터를 처리하는데 100GB의 메모리가 필요하다면, 우리는 그 솔루션을 사용할 수 없다.

과거 삼성전자에 다닐적에 그에 대한 사례를 들은 적이 있다. 디지털 카메라를 만드는 사업부가 아직 삼성전자에 소속되어 있을 무렵, 연구소에서 획기적으로 화질을 개선하는 알고리즘을 개발하여 사업부에 이관했다. 그런데, 사업부 개발자들이 받고서 엄청나게 욕을 했다. 이유는 화질은 획기적으로 개선되었지만 사진 한 장에 대한 프로세싱이 1분이 넘게 걸린다는 것이었다. 사진 한 장 찍고 1분을 기다려야 하는 카메라라면 누가 구입하려고 할까?

‘자료구조와 알고리즘’에 대한 이해를 등한시 하는 것은 적절한 동작 시간 안에 적절한 메모리로 데이터를 처리해야 하는 실무 세계에서 시간과 공간이라는 두 가지 개념을 무시하겠다는 것과 같다. 왜? 요즘엔 CPU도 빵빵하고 메모리도 크고, HDD는 엄청나게 싸니까… 물론, 시스템을 돌에서 물짜듯이 쥐어짜면서 구성할 필요는 없다. 시스템 대비 우리의 몸값도 싼 것은 아니다. 하지만, 비지니스의 성장에 따라 성능이 급격히 나빠지는 처리 로직을 짜는 것이 자원이 풍부하다고 해서 용서 받을 수 있는 일은 아니다. 최적 코드는 아니더라도 허용 가능한 성능을 갖는 코드를 짜는 능력은 매우 중요하다. 짧은 시간 안에 더 나은 코드를 짜는 것은 자료구조와 알고리즘을 얼마나 잘 이해하는가와 연결된다.

‘수학’이라는 과목은 사칙연산을 가르치기도 하지만, 본질적으로는 논리적 사고를 함양하는 것을 목적으로 한다. ‘자료구조와 알고리즘’ 역시 자료구조와 알고리즘을 직접 짜는 방법을 알려주려는 것이 가장 중요한 목적이 아니다. ‘자료구조와 알고리즘’은 시간 복잡도와 공간 복잡도를 계산하는 방법들을 알려주고 가장 많이 쓰이는 자료구조와 알고리즘을 통해 복잡도를 계산하는 방법을 이해시키는 것이다. 결국, 시간이 지나면 디테일은 사라지고 남는 지식은 각각의 자료구조와 알고리즘이 어떤 용도로 쓰이며 얼만큼의 시간 및 공간 복잡도를 갖는지 정도지만, 이를 통해 코드의 O(n)을 계산하는 능력을 얻게 된다. 이 지식과 능력을 바탕으로 어떤 자료구조를 선택해야 하는지와 나의 구현이 실제로 제품에 적용하기에 적합한지를 판단할 수 있는 것이다.


비전공자인 내가 ‘자료구조와 알고리즘’을 처음 접한 것은 ‘C로 구현한 알고리즘’ 책을 구입했던 대학 2년 때 이다. 하지만, 당시에는 읽어도 도통 무슨 소리인지도 모르겠거니와 8비트 마이크로 프로세서나 꽁냥거리던 환경에서는 필요성도 전혀 느끼지 못했다. 그 뒤에도 종종 책을 떠들어 보기는 했지만, 그냥 잡다한 지식을 쌓는다는 의미 외에는 없었고, 그마저도 언제나 O(n)이라는 표현 앞에서 무릎을 꿇었다. 사실 시작부터 O(n) 거리는데 뒤에 있는 자료구조나 알고리즘이랑 뭔 상관인지도 몰랐다. 그 뒤에 수년간 윈도우 프로그래밍을 할 때도 나에게 ‘자료구조와 알고리즘’은 프로그래머에게 별로 안 중요한 것이었다. 진정한 내용은 1도 모르면서 책들을 샀다는 뿌듯함만 안겨주는 그런 것이었다.

내 생각에 변화가 생긴 것은 2013년 LG전자에서 하광성과 함께 ‘손코딩뇌컴파일눈디버깅’을 시작하고, 후에 ‘별다방손코딩’ 퍼실리테이터를 하면서부터였다. 이제서야 O(n)으로 시작하는 내용과 책을 관통하는 의미에 대해서 알 수 있을 것 같다. 주변의 많은 사람들이 ‘손코딩뇌컴파일눈디버깅’의 영향을 받고 바쁜 와중에도 틈틈히 PS 트레이닝에 매진하고 있다. 그리고 나를 비롯한 몇몇 사람들은 수학 공부까지 다시 하고 있다. 그러면서 안타까워 한다. 지금 알고 있는 것을 그 때도 알았더라면…

이 글을 읽는 독자 중 ‘자료구조와 알고리즘’을 공부하고 싶다면, 다음과 같은 방법들을 추천하고 싶다.

1. ‘자료구조와 알고리즘’ 책을 공부한다.
2. ‘Top Coder’‘LeetCode’와 같은 트레닝 싸이트에서 꾸준히 문제를 푼다.
3. ‘별다방손코딩’ 모임에 나간다.


이제 마무리를 하겠다.

프로그래밍을 잘하기 위한 1단계는 코드가 하는 본질적인 일은 어딘가에서 받은 데이터를 가공해서 또 다른 데이터를 만들어 어딘가에 전달한다는 것을 이해하는 것이다. 데이터는 일종의 경계선이며 로직은 두 데이터를 잇는 파이프이다. 어떤 데이터들을 기준으로 코드의 경계를 나눌 것인가, 각 단계에 어떤 형태와 내용의 데이터가 존재해야 하는가와 데이터 사이의 형태 변환을 어떻게 가장 효율적으로 할 수 있을 것인가에 대해 끊임없이 질문을 던지고 답을 찾는데 신경을 써야 한다. 이것은 코드의 구조화와 가독성에도 많은 영향을 준다.