본문 바로가기
회사

테스트 프로그램 구현 제안 회고

by 개발고구마 2024. 3. 6.

개요

쇼핑몰 개발 및 유지보수 담당 업무를 하고 있는 와중에 너무 잦은 유지보수와 배포 이후 오류로 인해 지쳐가던 중 이전부터 TDD, 클린 코드 with Java 15기 (NextStep)를 듣게되었습니다.
관련 내용을 들으며 내가 담당한 구조에 적용해보고 싶어 본부장님께 제안을 드렸습니다.
결론부터 말하자면 본부장님과 나의 의견이 어느정도 달랐지만 나의 제안과 테스트 코드 적용을 맘에 들어하셔 포상 휴가를 받을 수 있었습니다.

테스트 프로그램 구현 제안서

아래는 제가 올린 제안 보고서를 가져왔습니다.

현재 쇼핑몰 로직은 계층형 아키텍쳐입니다.
계층형 아키텍쳐는 보통 비즈니스 로직이 서비스 객체에 뭉쳐있습니다.
목표를 원하는 것이 있고 그에 대한 로직들을 구현하게 됩니다.

쇼핑몰 로직을 예시로 들면
주문수집 API 호출 → 해당 쇼핑몰 수집로직 실행 및 수집 -> 파싱 후 파싱한 결과를 전달해주게 되어있습니다.

테스트는 mock객체 단위로 테스트하는 방법이 일반적입니다.
객체 단위 테스트는 전체 테스트이기 때문에 단위 테스트를 원한다면 각 비즈니스 로직을 함수별로 쪼개야 합니다.
내부 비즈니스 로직에서 테스트를 하기 위해 일부 목적이 같은 소스들을 함수화 한다면 한 서비스 안에 엄청나게 많은 함수가 생깁니다.

그래서 비즈니스 로직을 절차에 맞추지 않고 객체에 맞추는건 어떨지 생각해봤습니다.

현재 로직은 “수집해줘 그리고 전표 데이터를 파싱해서 보내줘” 가 큰 틀입니다.
수집과 파싱에 초점을 맞추고 목표를 위한 로직이 모두 한 곳에 모여있습니다. (절차기반)

수집과 파싱에 초점을 맞춘 것이 아닌 객체에 좀 더 맞춰본다면?
DDD(도메인 주도 설계) - DDD와는 궁극적인 목표가 다르지만 각 객체에 초점을 두는데 공통점이 있어 들고왔습니다.
OOP(객체 지향 프로그래밍) - 객체 단위 코드 프로그래밍이 아닌 객체 "지향" 프로그래밍에 초점을 맞췄습니다.

도메인이라는 것은 '영역'을 말한다. 그러니 비즈니스상에서 도메인이란 '업무의 집합'이라고 봐야한다. 
(출처 : https://yoonbing9.tistory.com/121)
예를 들어서 스타벅스는 '커피' 도메인이고, 버거킹은 '음식' 도메인이다.
(출처 : https://velog.io/@gowjr207/도메인-주도-설계DDD를-설명해보다)

.
.
일부 생략
.
.

위와 같이 정리하면 단위테스트할 때 구분도 더 쉬워집니다.
상품 기준으로 DTO 객체 생성 후 단위테스트해주는 소스(소스는 생략)

예외처리

로직이 에러가 나면 대부분 일시적인 에러 표출 또는, 전화 문의가 대다수 입니다.
솔루션은 try-catch로 여러 번 묶여 에러가 나오면 그 에러를 계속 가공해 주고 있습니다.
예외처리가 꼭 필요한 곳에서만 예외처리를 하고 가공하는 곳도 한 곳에서 가공해 중복 throw를 날리지 않는 방향으로 가공하려고 합니다(진행중)
위 조건이 처리된 후에 예외처리를 발생하게 한다면 Exception 종류+오류 문구로 어디에서 문제가 발생하는지 빠르게 파악 가능합니다.
추가로 고객에게 보여주는 문구도 이해하기 쉽도록 변경 예정입니다.

소스(정말 에러를 내줘야 하는 곳에 조건 예외처리 호출)
+예상이 가지 않는 부분만 try-catch로 처리

내 의견
원초적인 목표가 단위테스트 하고싶다 ( 사람이 하다보니 자꾸 생각지 못한곳에서 에러 터지는데 구조화 시키고 싶었다)
1. 단위 테스트하기 위해 함수별로 나눴다
2. 함수로 나누니 이 객체가 하는 역할이 너무 많다
3. 역할을 다른 객체에 나눠줬다
4. 역할에 대해 생각하다보니 이런 저런 생각들이 나 여러가지를 겸해서 구조 변경안을 생각해냈다

유지보수에 대한 초점보다 "단위 테스트에 대한 초점"을 더 어필(유지보수는 정확한 결과물을 내주기가 어려우니까)
유지보수는 "내가 당장 소스를 봐야하는 양"에 대해서 말씀드리기

현재 솔루션 
기능 추가보다 구조화가 필요
기능 "추가"에 초점을 두는 것이 아닌 현재 완성된 코드들을 "리펙터링" 해야 할 때

정리
1. 분류로 인해 단위테스트가 더 용이해진다.
2. 오류처리의 간소화로 인해 에러 위치 파악 및 고객 안내가 더 구체화된다.
3. 소스 분리, 단위테스트로 인해 새로 배우는 개발자의 러닝커브 및 유지보수가 쉬워진다.

위 제안을 드린 후 구축 방향을 다시 보고드렸습니다

구축 방향

a) 응답 Type 체크 함수
Http 응답에 대한 응답 type 체크
ex) json, html, xml 각 Type에 맞게 정상적으로 응답되었는지 확인.

b) 쇼핑몰별 필수 값 체크 Validate
ex) 쇼핑몰 Dto 구현시 특정 값은 무조건 있어야 되는 경우, 해당 값들을 사전 체크

=> 공통 값은 필수. 필수 값 정리 후 해당 값들을 체크하는 validate 생성
추후 데이터들을 Dictionary로 변경할 예정인데 이 때도 validate를 활용해 필수값이 들어오는지 체크

c) Dto 구성 (실제 파싱)
=> 절차가 복잡한 로직은 개별함수로 설정
테스트 프로그램은 결과값을 받고 해당 값이 정상적으로 잘나왔는지 확인

예시)
테스트케이스1 - 데이터가 없는 기본동작 - 결과가 정상적인지만 확인

var result = testMethod<DTO>(pm) as result; // 기존 소스 수정

Assert.AreEqual(result.StatusCode, "200");
Assert.AreEqual(result.ErrorMessage, "");

테스트케이스2 - 특정쇼핑몰 옵션명을 파싱해주는 복잡한 로직이 있을 때 - DTO결과값에 옵션명이 에러나지 않고 제대로 파싱 됐는지 결과값 비교로 확인

Assert.AreEqual(resultApiDto.OPTION, "빨강 +100원 / 파랑 +200원");

테스트케이스3 - 수집 중 특정 파라미터가 에러가 나는 상황 - 해당 에러가 제대로 나는지 확인

[TestMethod]
[Description("수집 결과 받은 후 옵션명 특이케이스 왔을 경우 아래 에러 반환")]
[ExpectedException(typeof(XmlException), "에러 예상으로 테스트 통과")]
public void GetSingleNodeInnerTextTest()
{
    //resultApiDto.OPTION = "[옵션명 : 타이어 "18인치]" <- 쌍따옴표가 홀수
    doSomething();
}

결과

구축 방향을 제안 후 결과 저는 각자 들어오는 입력값들이 틀어지지않고 정확히 들어오는게 중요해 각 파라미터 값마다 테스트 코드를 만들려고 했지만 오버 엔지니어링이라 하셔서 파일로 입력후 그 결과를 한꺼번에 확인하도록 컨펌되었습니다.

느낀점

추후 작성

참고 링크
1. https://velog.io/@jay/쉽게-말하는-계층형-아키텍처의-문제
2. https://yoonbing9.tistory.com/121
3. https://velog.io/@gowjr207/도메인-주도-설계DDD를-설명해보다