[번역] 잘가요, 클린 코드(Goodbye, Clean Code)

Posted on

최근 Dan Abramov 가 최근 자신의 블로그에 “Goodbye, Clean Code” 를 발행 하였습니다. 저는 이 글이 번역할 만한 가치가 있다고 생각하여 한국어로 번역을 하게 되었습니다. 도움을 주신 Glen 님께 감사의 말을 전합니다.

2020.01.20 추가: 원문에 번역된 한글 문서가 업로드 되었습니다. 해당 본문을 참고하시는 것을 추천합니다.


늦은 저녁이었습니다.

나의 동료가 방금 일주일 동안 작성한 코드를 체크 인 하였습니다. 우리는 그래픽 편집기 캔버스를 만들고 있었고, 가장자리의 작은 손잡이를 드래그하여 직사각형이나 타원형 같은 모양을 리사이즈 하는 기능을 구현하였습니다.

그 코드는 동작했습니다.

그러나 반복적이었습니다. (직사각형이나 타원형과 같은) 각 모양은 다른 손잡이 세트를 가지며, 각 손잡이를 다른 방향으로 드래그 하면 모양의 위치와 크기가 다른 방식으로 영향을 받습니다. 만일 사용자가 쉬프트 키를 누르고 있을 경우, 리사이즈 되는 동안 비율이 유지되어야 합니다. 많은 계산이 있었습니다.

그 코드는 대략 이렇습니다:

let Rectangle = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
};

let Oval = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeTop(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottom(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
};

let Header = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
}

let TextBlock = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
};

저 반복적인 계산은 나를 정말 귀찮게 했습니다.

그것은 깔끔 하지 않았거든요.

대부분의 반복 코드는 비슷한 방향 사이에 있었습니다. 예를 들어, Oval.resizeLeft()Header.resizeLeft() 와 유사합니다. 두 코드 모두 왼쪽 손잡이를 드래그하는 일을 처리하기 때문이었습니다.

다른 유사점으로는 같은 모양의 메소드들 사이에 있었습니다. 예를 들어, Oval.resizeleft() 는 다른 Oval 메소드들과 유사합니다. 이것들은 모두 타원형을 처리하기 때문입니다. 또한 TextBlock 이 사각형이기 때문에 Rectangle , HeaderTextBlock 간 중복이 존재했습니다.

나는 아이디어를 하나 냈습니다.

우리는 모든 중복을 제거하여 코드를 다음과 같이 그룹화 할 수 있었습니다.

let Directions = {
  top(...) {
    // 5 unique lines of math
  },
  left(...) {
    // 5 unique lines of math
  },
  bottom(...) {
    // 5 unique lines of math
  },
  right(...) {
    // 5 unique lines of math
  },
};

let Shapes = {
  Oval(...) {
    // 5 unique lines of math
  },
  Rectangle(...) {
    // 5 unique lines of math
  },
}

그리고 그것들의 행동을 구성했습니다:

let {top, bottom, left, right} = Directions;

function createHandle(directions) {
  // 20 lines of code
}

let fourCorners = [
  createHandle([top, left]),
  createHandle([top, right]),
  createHandle([bottom, left]),
  createHandle([bottom, right]),
];
let fourSides = [
  createHandle([top]),
  createHandle([left]),
  createHandle([right]),
  createHandle([bottom]),
];
let twoSides = [
  createHandle([left]),
  createHandle([right]),
];

function createBox(shape, handles) {
  // 20 lines of code
}

let Rectangle = createBox(Shapes.Rectangle, fourCorners);
let Oval = createBox(Shapes.Oval, fourSides);
let Header = createBox(Shapes.Rectangle, twoSides);
let TextBox = createBox(Shapes.Rectangle, fourCorners);

코드는 원래의 절반이 되었고, 중복은 완전히 사라졌습니다! 이제 깔끔합니다. 만일 우리가 특정 방향이나 모양에 대한 동작을 변경하려면, 모든 곳의 메소드를 수정하는 대신 한 곳에서 하면 됩니다.

벌써 늦은 밤입니다(시간 가는 줄 몰랐습니다). 나는 내 리팩토링 결과를 마스터에 체크 인 하고 잠자리에 들었으며, 동료의 지저분한 코드를 푼 것이 자랑스러웠습니다.

다음날 아침

… 예상대로 되지 않았습니다.

내 상사는 나를 1:1 대화에 초대하여 나의 수정사항을 되돌려 달라고 정중하게 요청하였습니다. 나는 경악했습니다. 이전 코드는 엉망이었고, 내 것은 깔끔 했습니다!

나는 마지못해 그 말을 따랐지만, 그들이 옳았다는 것을 알기까지 몇 년이 걸렸습니다.

이것도 과정입니다

“깔끔한 코드” 와 중복을 제거하는 것에 집착하는 것은 많은 사람들이 겪는 과정입니다. 우리가 우리의 코드에 자신감을 느끼지 못할 때, 측정될 수 있는 어떤 것에 자긍심과 직업적 자부심을 부여하는 유혹에 빠지게 됩니다. 이를 테면 엄격한 Lint 규칙, 명명법, 파일 구조, 중복코드 제거 말이죠.

당신은 중복 제거를 자동화 할 수 없지만, 연습하면 쉬워지기 마련입니다. 당신은 보통 수정 전후로 중복코드가 줄어들었는지 여부를 구분 가능합니다. 결과적으로, 중복 제거는 코드에 대한 객관적 지표가 향상되는 것 같이 느껴지게 됩니다. 불행히도, 그것은 사람들의 정체성을 망칩니다: “나는 깔끔한 코드를 작성하는 사람이야”. 그것은 강력한 자기 기만 중 하나입니다.

우리가 한 번 추상화를 배우면, 그 능력에 취해서, 우리가 중복 코드를 볼 때 마다 마구잡이로 추상화를 하고픈 유혹에 빠지게 됩니다. 몇 년의 코딩 후, 우리는 모든 곳 에서 중복을 보게 될 겁니다 - 그리고 추상화는 우리의 새로운 초능력이 되겠죠. 만약 누군가 우리에게 추상화가 미덕 이라고 말해 준다면, 우리는 그것을 먹어 치워버릴 겁니다. 이윽고 우리는 “깔끔함” 을 숭배하지 않는 다른 사람들을 안좋게 평가하기 시작할 테죠.

나는 이제 내가 했던 “리팩토링” 이 두 가지 측면에서 재앙이라고 봅니다:

  • 먼저, 나는 코드 원 작성자와 이야기 하지 않았습니다. 나는 대화 없이 코드를 재작성 하고 체크 인 하였습니다. 설령 그것이 향상되었다 하더라도(나는 더 이상 향상이라고 믿지 않지만요), 이것은 끔찍한 방법이었습니다. 건전한 엔지니어링 팀은 지속적으로 신뢰를 쌓습니다. 당신이 팀원의 토론 없이 코드를 재작성 하는 것은 함께 코드 베이스에서 효과적으로 협업하는 능력에 큰 타격을 불러 일으킵니다.
  • 두 번째로, 공짜는 없습니다. 나의 코드는 중복 코드를 줄이는 대신 유연한 코드 수정 능력을 희생해야 했고, 결과적으로 득보다 실이 컸습니다. 훗날 작업 중 각각의 모양들의 특수한 상황들을 위한 특별한 손잡이가 요구 된 적이 있었습니다. 나의 추상화는 코드 변경을 수용하기 위해 몇 배나 더 난해해져야 했지만, 원래의 “지저분한” 버전은 식은 죽 먹기였습니다.

내가 “더러운 코드"를 작성해야 한다고 말하고 있나요? 아닙니다. 당신이 “깔끔” 과 “더러움” 에 대해 말할 때 그 의미에 대해 깊이 생각해 볼 것을 제안하는 것입니다. 반발심이 느껴지나요? 옳음? 아름다움? 우아함? 당신은 저런 품질에 부합하는 구체적인 엔지니어링 결과를 나열할 수 있다고 얼마나 확신 하나요? 그런 엔지니어링 방법의 코드 작성과 수정이 정확히 어떤 영향을 끼치나요?

예전의 나는 확실히 그것들에 대해 깊이 생각하지 않았습니다. 나는 코드가 어떻게 보일 지 많이 생각 했습니다 — 하지만 나약한 사람들로 이루어진 팀과 어떻게 진화 했는지에 대해선 생각하지 않았습니다.

코딩은 여행입니다. 당신이 처음 작성한 코드 한 줄로부터 지금의 자신까지 얼마나 멀리 왔는지 생각해 보세요. 함수를 추출하거나 클래스를 리팩토링하여 복잡한 코드를 단순하게 만드는 방법을 처음 알았을 때 당신은 정말 기뻤을 게 분명합니다. 당신의 결과물이 자랑스럽다면, 깔끔한 코드를 추구하는 유혹에 빠지게 될 것입니다. 당분간은 클린 코드를 계속 하세요.

하지만 거기서 멈추지 마세요. 클린 코드의 광신도가 되지 마세요. 클린 코드는 목표가 아닙니다. 그것은 우리가 처리하는 시스템의 엄청난 복잡성을 이해하려는 시도입니다. 클린 코드는 코드 수정이 코드 베이스에서 어떤 영향을 끼치는지 확신할 수 없지만 당신이 무지의 바다에서 지침을 필요로 할 때의 방어 기제입니다.

클린 코드가 당신을 인도합니다. 그리고 놓아주세요.


원문: https://overreacted.io/goodbye-clean-code/

번역: 김문수, Glen