Lilyrs

너의 이름은

공채로 들어온 신입에게 당장 많은 업무를 주긴 어렵다. 보통 작은 기능이나 업무부터 수정해보라고 샌드박스 환경을 마련해준 다음, 직접 코드도 분석해보고 어떤 서비스인지 문서화된 자료를 바탕으로 판단해보는 시간을 우선적으로 마련해주기 마련이다.

매해 신입이 내가 있는 곳에도 들어오긴 하지만, 회사를 떠나기 전에 마지막으로 받은 신입이라 여러 요소를 꼼꼼히 지켜보고 알려주고 있다. 문제를 해결해나가기 위해 분석하는 과정부터, 프로그래밍, 회사 생활까지. 갓 첫 사회생활을 시작한 신입에게 너무 많은 걸 요구할 순 없지만 이 시기가 아니면 배울 수 없는 게 있다.

바로 프로그래밍에 있어서 ‘습관’. 변수명 짓는 것부터 코드 스타일이나 개발 방향 등등.

요즘은 어지간하면 4년제 대학교에 컴퓨터 관련 학과를 졸업하든 부트캠프를 통해서 교육을 이수하고 넘어오는 편이지만, 실제로 교육 기간 중에 자기 자신만의 프로젝트를 진행해보면서 많은 경험을 쌓아 자신만의 규칙을 세운 사람은 생각보다 많지 않다. 반드시 고생을 해야하는 이유는 없지만, 이 과정을 거침으로써 무술에서 말하는 ‘근육에 불필요한 긴장을 빼고 필요한 동작과 상대에 집중한다’가 이루어지는 것이다.

이번에 들어온 신입 직원에게, 업무 중에 사용할 커맨드라인 도구를 제작해달라는 시나리오를 주고 코드를 만들어달라고 과제를 준 적이 있었다. 그 과제에선 커맨드라인에 주어진 파라미터들을 순서대로 받아와 처리해야할 필요가 있었는데, 최종 결과를 보니 이런 코드를 작성해둔 상태였다.

내부 교육용 자료로 만들어둔 과제라 전체 내용을 공개하기 어려운 점 먼저 양해를 구한다. 문제의 코드는 Java로 작성되었다. 파일 내용을 읽어오는 기능은 String readAsString(String path, String srcEncoding) 라는 가상의 메소드로, 파일에 기록하는 건 boolean saveFromString(String path, String dstEncoding, String contents) 라는 가상의 메소드로 대체하겠다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
명령행 파라미터로 원본인코딩, 목표인코딩, 입력파일명, 출력파일명을 순서대로 받아,
텍스트파일의 인코딩 변환 작업을 수행한다.
*/

public static void main(String[] args) {
// ... omit above ...
String s = readAsString(args[2], args[0]);

if (saveFromString(args[3], args[1], s)) {
System.out.println("저장되었습니다.");
} else {
System.err.println("저장되지 않았습니다.");
}
// ... omit below ...
}

과제를 마친 후 코드를 확인해볼 때 작성된 코드는 이런 식으로 작성되어있었다. 일단 기능상으로 큰 문제가 없었기에, 그 부분은 추후에 보기로 하고 내가 주목한 부분은 args[n] 으로 직접 명령행 파라미터에 계속 접근하고 있는 부분이었다. 이 뒤에도 꾸준히 저런 식으로 이어진 건 당연했다.

그래서 나는 두 가지를 물어보았다. 하나는,

만약에 제가 명령행 옵션을 파싱할 수 있는 라이브러리를 사용하도록 요구조건을 수정하면 코드를 어떻게 수정하시겠습니까?

그리고 다른 하나는,

3주 뒤에 이 코드에 대한 개선 요청을 할텐데, 그때도 args[0], args[1], args[2], args[3] 이 무엇인지 기억할 수 있습니까? 파라미터의 순서가 바뀌면 어떨 것 같습니까?

였다. 이것을 지적한 이유는, Java의 배열(Array)의 내용물은 기본적으로는 같은 타입, 즉 Data structure with homogeneous elements라는 것이다.[1][2] 개별 데이터는 한 타입으로 되어있고, 배열에 저장된 순서와 값 자체를 제외한 그 어떤 정보도 개별 데이터를 식별하는데 쓰일 수 없다. args[0] 은 원본 인코딩이고 args[1] 은 목표 인코딩이라고 정의한 건 사람 입에서 나온 약속이다. 즉, 이렇게 하기로 약속한 사람이 말한 지식이 어딘가에 정리되어있지 않으면 각 변수의 역할이 무엇인지 알기 어려워진다.

별도로 명령행 옵션을 파싱할 수 있는 라이브러리를 도입하지않고, String[] args 를 직접 처리해야만 한다면, 내가 권장하는 사항은 각 항목을 별도 변수명을 만들어 별명을 지어달라는 것이다. 예를 들면,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
명령행 파라미터로 원본인코딩, 목표인코딩, 입력파일명, 출력파일명을 순서대로 받아,
텍스트파일의 인코딩 변환 작업을 수행한다.
*/

public static void main(String[] args) {
// ... omit above ...
String srcEncoding = args[0];
String dstEncoding = args[1];
String srcFileName = args[2];
String dstFileName = args[3];

String s = readAsString(srcFileName, srcEncoding);

if (saveFromString(dstFileName, dstEncoding, s)) {
System.out.println("저장되었습니다.");
} else {
System.err.println("저장되지 않았습니다.");
}
// ... omit below ...
}

이렇게 이름을 대응시켜주면, 제일 먼저 언급했던 코드가 보이는 것처럼 정리되었다. 계속 args 배열에 대한 인덱스 번호만 오가던 코드에서, 그 값이 구체적으로 어떤 역할을 수행하는지 비교적 알아보기 쉽게 정리된 코드로 바뀌었다.

항상 이름을 짓는 일은 고통스러운 일이다. 그러나 절대 게을리 해선 안된다. 좋은 이름을 짓기 위해 작명소에 찾아가서 여러가지 이야기를 듣는 것처럼, 우리는 좀 더 좋은 이름을 변수나 메소드, 클래스 등의 네임스페이스 구성에 쓰기 위해 노력해야한다. 혼동을 줄 수 있는 용어는 바꿔야하고, 서로 다른 사람이 코드를 작성하더라도 일관성 있게 이름을 짓는 사전도 필요하다.

결국 이러한 노력들이 모여 코드를 보기 좋게 만들기도 하고, 이해하기 편하게 만들기도 한다. 작은 습관이지만, 지금 당장 당면한 과제가 이걸 하게 되면 못할 정도로 심각한 상황이 아니라면 꾸준히 고민하고 자신만의 좋은 방법을 고안해내길 부탁한다. 그리고 팀 단위로 일하게 된다면, 그 팀의 원칙을 존중할 수 있도록 한다.


  1. 1.항상 homogeneous하진 않은 자료형의 예로는 Python에서 제공하는 리스트를 들 수 있다. 만약 일관되게 한 타입만을 갖는 리스트를 구현하고 numpy 나 다른 리스트 구현체를 써야한다.
  2. 2.Java의 경우 Object 타입으로 레퍼런스를 캐스팅해서 배열에 넣으면 '값'을 기준으로 생각했을 때는 Homogeneity 가 무너졌다고 생각할 순 있지만, 이들 레퍼런스의 타입들만 봤을 땐 모두 Object 이므로 Homogeneity 를 지키고 있다고 볼 수 있다고 생각한다.