image

실제 사용할 경우에는 크게 문제되지 않지만 개발 도중에는 상당히 불편하다. 주요 기능을 개발하기 위해 매번 테스트용 이메일과 비밀번호를 입력하고 들어가는 것은 생각보다 생산성을 훨씬 떨어뜨린다. DB 작업으로 테스트 데이터가 초기화된 다음이라면 테스트 계정을 또 만드는 것이 한 보따리 작업일 수도 있다.

우리는 바보가 아니므로 다음과 같이 질문할 수 있다.

주요 기능부터 만들고 마지막에 로그인(계정) 기능을 만들면 되잖아요.

하지만 누리의 주요 기능에는 다수의 사용자가 상호 작용하는 서버 접속, 대화 등의 기능이 포함되어 있으므로 계정 관련 기능이 먼저 필요하다.

그럼 다른 방법이 있다.

자동으로 이메일과 비밀번호를 테스트용 데이터로 입력해 두면 되죠.

나쁘지 않은 방법이다. 특히 여러 명이 필요 없는 대다수의 초기 기능을 만들 때에는 아마도 가장 빠르게 이런 방법을 사용할 것이다. 하지만 둘 이상의 접속자가 필요한 기능을 테스트할 때에는 여전히 한 개를 제외한 나머지 인스턴스들은 죄다 바꿔줘야 하는 점에 변함이 없다.

좀 더 나아가자.

인스턴스 별로 각기 다른 테스트용 이메일을 사용하게 하는 건 그리 어렵지 않아요.

자, 조금 신중해질 시점이 왔다. TDD가 어플리케이션 개발에 대단한 기여를 했지만 그 역시 여러 약점을 갖고 있었는데 그 중 하나가 바로 테스트 데이터의 오류에 대한 검증이다. 이 부분을 건드리는 순간 분화구 뚫는 용암처럼 온갖 이론과 철학들이 분출된다. 우리의 이야기가 TDD는 아니지만 지금 이 문제에 집중한다면, t1@mail ~ t10@mail 을 인스턴스 별로 할당하고 집어넣은 것이 기능 개발에 어떤 부작용을 발생시킬 지 고민하지 않을 수 없다는 것이다. 단위 테스트를 통해 어느 정도 해결 가능한 면이 있지만 저 정도의 기능 개발은 필연적으로 통합 테스트 단계를 요구한다. 이 단계가 누락되면 나도 모르게 기능 개발에 테스트 데이터가 감안된 사양이 나와버리고, 저 단계를 집어넣으면 기능 개발에 상당한 시간이 소요된다.

그렇다면 다른 주요 기능의 개발과 테스트를 위해 편리함을 더하고자 테스트 데이터를 넣어 문제 가능성을 높이는 찝찝함을 다른 방식으로 혹은 더 영리하게 피할 수 있을까?

개발 중에만 계정 정보 없이 임시로 생성된 정보로 바로 실행할 수 있게 하면요?

이후의 진행을 두 가지로 나누어볼 수 있겠다. 접속 버튼을 눌렀을 때 기계적인 데이터 생성을 통해 계정 정보를 임시로 만들어 넘겨주는 것과, 계정 정보가 없을 때에 대한 예외 처리를 일일이 작성하는 것. 전자의 방식은 사실상 바로 직전에 이야기한 테스트 데이터에 대한 문제를 고스란히 갖고 있으므로 그다지 진보하지는 못했다. 무작위로 여러 데이터를 조합해서 계정 정보를 만들면 오히려 의도하지 않았던 데이터가 사용되어 테스트에 노이즈가 발생한다. 후자의 방식은, 얼핏 보면 초보 프로그래머나 저지를 법한 막무가내식 작업으로 보일 지도 모르지만 서버와 클라이언트 모델을 사용하고 해킹의 위협이 항상 존재하는 프로그램에서는 방어적 기법으로 도배되는 경우가 많이 있다. 물론, 진짜 로직적인 이유로 데이터가 깨졌을 때와 우리가 의도적으로 데이터를 주지 않은 경우를 구분할 수 없으므로 이 방식이 올바르다는 말은 아니다.

약간 복잡한 이야기로 전개해보자.

세 번째 방법에 데이터 추상화를 끼얹어서 테스트 데이터와 실제 데이터를 구분하면 되겠네요

데이터의 종류도 하나의 데이터로 볼 수 있다. 이에 초점을 맞추면 ‘테스트’와 ‘실제’ 라는 속성은 데이터를 구분하는 주요한 데이터 중 하나다. 따라서 이를 데이터 내의 속성으로 추가하든 상속 구조를 차용하든 둘을 분리하여 처리하는데 활용할 수 있다. 그러나 이 구조는 생각보다 개발 비용이 크다. 데이터는 데이터일 뿐, (올바르다고 이야기하는 구조에서는) 보통 기능을 포함하지 않기 때문에 바로 위에서 이야기한 예외 처리 비용처럼 모든 기능이 테스트용일 경우와 아닌 경우에 대해 구현되어야 하는 문제가 발생한다. 여기에 계정 정보가 잘못되었을 때의 처리를 더하면 모든 기능이 최소한 세 가지 경우에 대해 구현되어야 하는데 마스터 계정, 손님 계정, 관전자 계정 따위가 더해지면 문어발도 부족할 지경이다. 이를 도식화하면 아래와 같이 간결하게 나오는데,

image

Method가 옆으로 무한정 늘어나는 모습이 아름답게(=객체지향스럽게) 보일 수도 있지만 이는 개발 비용을 계속 증가시켜서 투입할 수 있을 때에나 가능한 방법이라고 하겠다. 사양 변경에 맞춰 코드 변경이 전체에 걸쳐 발생하므로 QA가 할 일도 줄어들진 않을 것이다.

그렇다면 저 약점을 극복할 수 있는 방법이 있으니 더더더 복잡한 이야기를 해보자.

궁극의 오의 DDD를 끼얹겠습니다. 코드 변경 없이 동작할 수 있게 하면 되는 거죠?

TDD와 마찬가지로 DDD 또한 개발 방법론에 끼친 영향이 (현재진행형으로) 엄청난데 약점도 거대한 편이다. 우리가 최초 이 이야기를 시작한 이유를 다시 상기해보자. 우린 다른 기능의 개발 및 테스트를 효율적으로 진행하기 위해(=테스트 비용을 줄이기 위해) 과정을 간소화하는 방법에 대해 이야기하고 있다. TDD를 도입하거나 DDD를 도입하거나 (또는 DOP를 계획하거나) 하는 등의 일은 프로그램 설계 단계에서 미래를 결정 짓는 정도의 일이다. 애초에 프로그램이 DDD로 진행되고 있다면 생각해볼 법한 방법이나, 혼자서 만들고 있다면 이 정도 규모로 설계한 것을 재고하길 권한다. 그 시간에 하드코딩하는 것이 훨씬 빠르다.

위와 같은 과정은 단순한 기능 개발 하나에서도 뭔가를 해결하기 위해 고민하면 흔하게 접할 수 있는 문제다. 그런데 결국 해결하지 못하고 고민이 계속 된다. 그 이유가 무엇일까?

네가 너무 완벽한 해결책을 요구해서죠.

완벽한 해결책이라는 단어를 좀 더 풀어서 설명하자면 다음과 같은 속성을 지녔다고 생각한다.

  • 미래에 다른 작업이 추가/수정/삭제 되더라도 영향을 주거나 받지 않는 (시간 조건)
    • 미래에 추후 작업이 발생하지 않는(=유통기한이 영구적인)
  • 부분 해결이 아닌, 모든 상황에 대한 해결이 가능한 (상황 조건)
    • 자동 작업 외 수동 조작이 추가로 필요하지 않는
  • 비개발적인 상황 조건을 고려하지 않고 모두 적용할 수 있는 (자원 조건)
    • 1인 개발일 때와, 5인 이하 소규모일 때와, 50인 이상 대규모일 때 모두 사용 가능한
    • 개발 시간이 없을 때나 넉넉할 때나 모두 사용 가능한

자, 이 포스팅의 주제에 대해 이야기할 시간이 왔다. 우리가 비록 개발자지만 이러한 문제 해결에 대해서, 혹은 설계에 대해서는 0과 1의 딱 떨어지는 결과가 아니라 굉장히(굉장히 굉장히) Fuzzy한 선택과 집중을 할 수 있는 자세가 필요하다는 것이다. 그 중 선두주자는 유통기한이다. 재작업을 좋아하는 개발자가 별로 없을 거라고 생각하지만, 중요한 것은 그 재작업의 빈도인 것 같다. 보통 두 번째와 세 번째 이유는 당연히 고려하면서도 자신의 코드가 언제까지 유지되는가에 대한 부분은 최소한 자신이 해당 프로젝트를 마감할 때까지는 변경이 없을 거라고 (또는 변경이 없게 만들 거라고) 생각을 많이 하는 듯 싶다. 그리고 그 부분이 고민을 끝나지 않게 만든다.

하지만 위의 사례에서, 일단 두 번째 방법을 택한 다음 1시간 정도를 투자해서 앞으로 4개월 정도 버틸 수 있다면 채산성이 훌륭하다고 본다. 그렇게 기본 기능을 만들고, 더 복잡한 테스트 데이터를 요구하는 시점에 좀 더 작업해서 또 y개월을 버틸 구조를 넣고 하는 식이다. 처음부터 미래를 예측하여 많은 고민을 하는 것은 그렇게 좋지 않다고 이야기하지만, 바로 그것을 버리지 못하는 경우를 많이 보아와서 마침 간단한(?) 사례가 있기에 정리해 보았다.

뭐랄까, 간만에 개발 일지다운 글이 된 것 같다. (정작 개발은 안 하고?)

Posted by OOJJRS
,