본문 바로가기
Study/객체지향 시스템 디자인 원칙

[객체지향 시스템 디자인 원칙] 내 생각 Q&A - 6장. 외부 의존성과 인프라 다루기

by Nahwasa 2025. 7. 16.

목차

     

      이 글은 ['객체지향 시스템 디자인 원칙'을 읽으며 정리한 내 생각 - LLM과의 Q&A 기반 사고 확장] 글을 챕터별로 나눈 시리즈 중 하나 입니다. 전체 흐름을 보고 싶거나, 다른 챕터를 확인하고 싶으시다면 위 통합글을 참고해주세요.

     

      이 글은 책 내용을 요약하거나 정리한 글이 아닙니다. 책을 읽고 난 뒤 제 생각을 중심으로 정리한 글 입니다. '객체지향 시스템 디자인 원칙'을 읽으며, 느낀점이나 좋았던 점 및 책에 제시된 연습문제를 LLM에게 알려줬습니다. 이를 바탕으로 제 생각을 기반으로 책의 연습문제를 변형하거나, 새로운 질문을 생성하게 했습니다. 그 후 제가 그 질문에 답하고, LLM과 토론하는 방식을 사용해 사고를 확장해봤습니다. 이 글은 그 질문과 답변을 모아둔 글 입니다. 자세한 작성 배경은 통합글에서 확인하실 수 있습니다.

     

     Q1, Q2와 같은 질문 외의 모든 답변은 LLM을 쓰지 않고 제가 직접 답변한 내용입니다. 이후 LLM과 토론하며 생각이 조금씩 바뀐 부분들도 있으나, 최대한 초안 답변으로 올렸습니다.

     제 생각을 적은 것으로, 일반적인 개발자들의 생각이나 사실과 다소 다를 수 있습니다.

     

     


     

    6장. 외부 의존성과 인프라 다루기

    Q1. 도메인을 우회하고 DB 직접 접근해도 될까?

    배경:

    책에서는 “애그리게이트 루트를 통해 처리하는 것보다 DB에서 직접 처리하는 게 효율적으로 보인다면,

    그 전에 도메인 설계가 잘못된 건 아닌지 먼저 검토하라”고 말했지.

    세영도 이 주장에는 논리적으로 동의하지만,

    실제로는 “도메인 로직을 DB에서 처리하는 것” 자체에 거부감을 가지고 있어.

    DB 접근을 완전히 금기시하는 건 아니지만,

    검증, 상태 판별, 일련의 비즈니스 로직을 쿼리나 DB 트리거 등으로 넘기는 건 매우 꺼리는 편이야.

     

    질문:

    • 만약 팀원이 “이건 굳이 애그리게이트까지 안 가도 되니까 DB에서 직접 처리하자”고 제안한다면,
    • “그게 도메인 로직인지 아닌지”는 어떤 기준으로 판단해?
    • 예를 들어 단순한 상태 변경 쿼리나 batch update도그런 판단 기준이 명확히 있는지 궁금해.
    • 도메인 로직처럼 느껴질 수 있는데,
    • 또, 효율과 설계의 균형에서 어디까지는 타협 가능하고, 어디부터는 안 된다고 생각해?

     

    내 답변

      설계에 정답은 없어. 팀원의 명분이 논리적이라면 팀의 합의로 일반적으로 안좋다고 보여지는 것이더라도 협의 결과에 따라 수용해도 괜찮다고 생각해. 어쨌든 그렇더라도, 도메인 로직이라면 DB에서 처리하는건 옳지 않다고 보긴 해. 즉, 직전에 괜찮다고 생각한건 구조를 바꿔서 해결해볼 수 있다이지, 도메인 로직이 확실히 맞다면 이건 생각을 바꾸긴 쉽지 않아. 이게 DB에서 뭔가를 처리하는게 나쁘다는 얘긴 아니고, 서비스의 핵심 로직이 DB에서 처리되는걸 싫어하는거야. 이후 서비스 확장될 때 스케일아웃하기도 힘들고, 일단 테스트나 디버깅도 어려워지지. DB에서 쿼리로 작업 시 매직넘버가 들어갈 확률도 너무 높아져. 이후 유지보수자도 DB에 있는 로직을 도메인 로직이라고 생각하기 쉽지 않아서, 맘편히 바꿔버릴수도 있어.

     

      참고로 이론적으로만 DB에 로직이 있는게 싫다고 생각한건 아니고, 개발자 처음할 때 파견업체에서 시작했거든. 그 때 직접 해봤어. 심지어 당시 PL급이 "비즈니스 로직은 DB 프로시져에 전부 있어야한다"라고 한게 기억나. 이미 팀의 합의가 저렇게 되어있으니 어쩔수없이 따르긴 했고, 나도 지식이 그리 많지 않았을 때지만 당시에도 뭔가 위화감은 계속 들었었지. 지금은 뭐 명확히 왜 싫어하는지 이유를 댈 수 있게 된거고.

     

      위에서 말한 ‘구조를 바꿔서 해결해볼 수 있다’에 대해서는 예를들어 뭐 애그리거트 루트에 있던 로직인데 너무 효율성이 안나오는거야. 애그리거트 루트에서 애그리거트 100만개 가지고 처리하는 로직이 있다고 쳐봐. 메모리나 시간복잡도 전부 저 멀리 가겠지. DB에서 처리하거나 배치처리하는게 맞을꺼야. 근데 그럼 이게 도메인 로직이 맞냐부터 생각해보는거지. 사실 설계에 정답은 없어서, 일반적으로 생각을 약간만 틀면 ‘응 이런식으로 생각하면 DB에서 처리해도 전체 시스템 정합성에 문제없게 가능하겠다’ 식으로 뺄 수 있어.

     

      그럼 도메인 로직인지 아닌지 어떤 기준으로 판단하는지도 생각해볼께. 사실 아직 내가 코드를 설계하며 진행한 경험이 많은건 아니니까, 아직은 좀 직관적으로 판단하는 것 같아. 대충 다이어그램 그려봤을 때 ‘얘가 제대로 안되면 언젠가 서버나 나 둘 중 하나는 터지겠는데?’ 싶은걸 도메인 로직으로 보는 것 같네 ㅋㅋ 또는 뭐 ‘이 부분은 테스트 코드 안짜면 잠 못자겠는데?’ 싶은거. 뭐 글로야 여러가지 적을 수 있겠지. 서비스의 핵심 가치를 반영하고 있다거나, 서비스에서 메인 규칙을 반영한다거나, 비즈니스 로직의 상태 변경이나 제약 조건과 관련되었다거나, 그게 망가지면 서비스의 신뢰도 자체에 문제가 생긴다거나. 근데 아직 내 수준에선 직관적으론 알고 있지만, 누군가 이건 도메인 로직 아니죠. 이럴 때 명확하게 그게 맞다고 술술 말을 하진 못할 것 같아.

     

      그러니 어디까지 타협 가능하고 어디부터는 안된다고 생각하는지도 좀 두루뭉술해. 내가 처음에 도메인 로직으로 생각했더라도, 이후 얘는 그냥 밖으로 빼도 되겠는데? 싶은것도 있고. 밖에 있더라도 나중에 어.. 얘는 안쪽으로 들여와서 보호하는게 맞겠다 싶기도 해. 결국 서로의 명분을 가지고 토론해서 결정하는게 맞겠지.

     

      확실히 이 질문에 답변하다보니, 내가 이 기준에 대해서는 주관이 뚜렷하지 않다는게 나도 느껴지네. 이 부분은 좀 더 공부하고 생각해봐야겠어.

     

     

    Q2. “자신이 소유한 것만 의존하라”의 경계

    질문:

    • 현재 프로젝트에서 ‘남의 것’을 의존하고 있는 코드 중 **“굳이 감쌀 필요 없다고 생각하는 것”**이 있다면 예시를 들어볼 수 있어?
    • 반대로, “이건 꼭 감쌌어야 했는데, 그냥 쓴 걸 나중에 후회했던 경험”이 있다면?
    • 그 두 가지를 나누는 기준은 뭐야? 클래스의 복잡성, 유지보수 가능성, 아니면 도메인 오염 여부?

     

    내 답변

      자바의 장점이자 단점이라 생각하는게 하위호환성을 왠만하면 지켜준다는 점이야. 그래서 자바 라이브러리 부분은 굳이 감쌀 필요 없다고 생각해. 변경 가능성이 매우 적거든. 그리고 자바와 달리 버전업 시 자주 deprecated 되거나 삭제되는 편이긴 하지만, 그래도 스프링 프레임워크의 라이브러리도 괜찮다고 봐. 일반적으로 스프링부트나 스프링 버전업은 별도 업무로 일정잡고 작업하는만큼 갑자기 바뀔 일이 거의 없다고 봐. 프레임워크나 메인 언어의 라이브러리를 ‘남의 것’으로 판단하고 래핑하기엔 피곤함이 더 크다고 생각해.

     

      반면 이 글을 읽으면서도 느낀건데, 사실 json 관련 라이브러리들이 상당히 많거든. 그리고 막 JSONObject, JsonObject 등등 비슷한데 약간씩 다른 이름의 클래스들이 존재하지. 내 경우 지금 생각해보니 왜인지 모르겠지만 감쌀 생각을 안했었어. 아무래도 자주 쓰는 외부 라이브러리라 기준이 무뎌졌던 것 같아. 외부 라이브러리에 의존성 생기는 것 같고, 코드에 대한 개발자의 책임이 적어지는 느낌이라 lombok도 잘 안쓰는 내가 json 같은건 그냥 막 썼던 셈이지. 이건 다음부터 별도로 처리하는게 좋아보여. 사실 개발자 초기엔 막 json 파싱같은 재밌는걸 라이브러리한테 맡긴다고? 이 재밌는걸 안만드는건 손해다. 내가 직접 짠다! 이런적도 있는걸보면 나도 많이 발전한듯 ㅋㅋ

     

      나누는 기준은 아무래도 '이후 유지보수 시 변경 가능성'이 가장 큰 것 같아. 뭐 도메인 오염이나 클래스 복잡성도 전부 유지보수 가능성과 관련있으니 하나로 퉁치면 될 것 같아. 예를들어 도메인이 외부 라이브러리에 오염됬을 때 문제는 외부 라이브러리가 변경되었을 때, 도메인까지 수정해야 된다는 점이지. 자바나 프레임워크의 라이브러리는 이후 변경 가능성이 적어. 혹시 변경될 경우, 보통 단순 유지보수가 아니라 업무로 잡고 일정잡고 진행되는 꽤 큰 작업이야. 그러니 괜찮다고 봐. 물론 LTS 버전 쓸 때 얘기겠지. 반면 서드파티 라이브러리 같은 경우 심심치 않게 변경될 수 있어. 비슷한 기능을 가진 라이브러리들이 많고, 기능 지원여부나 더 나은 기능등을 이유로 유지보수 시 변경될 수 있어.

     

     

    Q3. 인프라 코드와 도메인 코드의 분리 실전 감각

    배경:

    책에서 “도메인과 인프라를 분리해야 한다”는 이야기를 중심으로,

    너는 이미 DI나 포트-어댑터 구조를 적극 활용하고 있었고,

    프레임워크나 라이브러리의 예외가 도메인 로직에 노출되지 않도록 처리하는 것도 신경 쓰고 있다고 했어.

    질문:

    • 도메인 코드에 스며든 인프라 코드(혹은 예외, 클래스)를 나중에 ‘지우기 힘들어졌던’ 경험이 있을까?
    • 만약 지금 신입에게 “도메인과 인프라 코드를 분리해야 한다”고 설명해줘야 한다면,
    • 어떤 기준이나 예시를 들어서 설명할래?

     

    내 답변

      나중에 지우기 힘들었다기보단, 이걸 지우긴 해야되는데 어떻게 지울지 생각이 안난 경우가 있었어. 도메인 내에 암호화된 데이터가 필요한 경우가 있었어. 뭐 물론 지금 생각해보니 애초에 도메인 객체에 암호화된 데이터가 필요했던거 자체가 문제긴 한데, 아무튼 당시엔 그랬으니 이건 넘어갈께. 이 때, 암만 생각해봐도 ‘암호화가 되어있어야 한다’ 자체가 도메인 객체의 책임으로 느껴지는거야. 심지어 어떤 암호화 알고리즘을 쓰느냐도 도메인 로직이라는 착각도 있었지.

     

      즉, 외부에서 처리할게 아니란 생각이었지. 근데 그렇다고 암호화 로직을 직접 개발하는건 바퀴를 새로 만드는꼴이잖아. 이미 잘 만들어져있는 것들이 많은데 말야. 그렇다고 도메인 객체 자체에 추상화도 안된 외부 라이브러리를 넣을수도 없고.. 그렇다고 주입으로 받아봤자 아무튼 결국 도메인에 외부 라이브러리 간접적으로라도 결합도 생기는거라 이것도 아닌것같고.. 주변 개발자와 토론하며 어떻게 처리할지 고민했던걸로 기억해. 당시 결국 어떻게 처리했는지가 기억이 안나네. 내부에 외부 라이브러리에 대한 결합도를 남겼을 것 같진 않고, 타협해서 암호화된 값을 VO로 보고 서비스에서 처리해서 넣어줬던 것 같아.

     

      지금 생각해보면 도메인이 굳이 기술적 수단(암호화 알고리즘 등)까지 판단할 필요는 없는데 당시 책임을 나누는 경계에 대한 감각이 부족했던 것 같아.

     

      도메인과 인프라 코드를 분리해야 한다를 설명하려면, 그 반대되는 상황을 보여주는게 가장 베스트지. 외부의 변경이 도메인으로 전파되어 기존 도메인 로직에 대한 테스트 코드를 다 갈아엎고 정합성 새로 맞춰야하는 케이스를 보여주면 될 것 같아. 그리고 역으로 질문을 던져보면 좋을 것 같아. 예를들어 '여기서 Json 라이브러리를 다른걸로 바꾸면 어떻게 될 것 같아?'

     

     

    Q4. “에러는 고수준 도메인 예외로 감싸야 한다”의 적용 한계

    배경:

    책에서 SDKBot의 예외를 예로 들며, 외부 예외를 고수준 도메인 예외로 감싸야 한다는 조언을 했지.

    세영도 이 부분에서 ‘실제로 그렇게 처리하고 있다’고 했고, 테스트의 용이성이나 코드 오염 방지 측면에서도 동의했어.

    질문:

    • 실무에서 모든 외부 예외를 고수준 예외로 감싸는 게 현실적으로 힘들 때가 있잖아?이런 타협을 언제 허용하고, 언제는 반드시 감싸는지 기준이 있다면?
    • 예를 들어 공통 유틸에서 예외를 바로 던지거나, 중간 레이어에서 catch 없이 propagate 하는 경우.
    • 또, 너는 도메인 예외 클래스 설계할 때 어떤 기준으로 이름과 구조를 정해?
    • (ex: DomainException, ExternalServiceException, JsonParsingException 이런 구분을 어떻게 잡는지)

     

    내 답변

      실제로 최근 프로젝트에서도 별도로 리팩토링할만한거 메모하며 코드 짤 때, 제일 많이 적었던게 특정 지점에서의 Exception 처리 문제였어. 예를들어 외부 API 호출하는 코드에서 암호화 모듈을 썼다고 해볼께. 특히 암호화 모듈의 경우 오래된 라이브러리들이 많아. 그리고 요즘 라이브러리들은 checked exception을 지양하는 반면, 예전 라이브러리들은 checked exception을 많이 쓴 경우가 꽤 있지.

     

      이 경우 예외가 상위 모듈까지 올라간다면 외부 API를 사용하려는 클라이언트 코드에서, 갑자기 InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException 이런 익셉션들을 처리해야 되는 상황이 되버려 ㅋㅋㅋ. 뜬금없지.

     

      참고로 실제 내 리팩터링 지점이었고, 다음과 같이 처리했어. 우선 테스트코드로 null이 들어간 경우를 포함해 우리 서비스에서 사용될만한 다양한 암호화 케이스를 전부 테스트했어. 그리고 생각해봤지. 애초에 InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException 이런 익셉션이 발생했을 때, 시스템적으로 이걸 복구할 방법이 있는가? 당연히 없지. 즉, 해당 에러들은 사실 코드를 잘 짜고 테스트코드에서 문제가 없었다면, 더이상 판단하지 않아도 되는 에러야. 저걸 보고 복구하는게 아니라 애초에 발생하면 안되는 에러들인거지. 따라서 그냥 암호화 모듈쪽에서 상위로 아무것도 전파안하도록 처리했어.

     

      또, 우리의 메인 서비스 로직에 외부 API를 불러야 하는 경우가 있었어. 마케팅 동의에 따라 쿠폰을 발송하는 부분이었어. 이 때, 우리의 메인 서비스의 로직의 성공 여부가 쿠폰 발송 성공여부에 영향을 받아야 하냐? 라고 생각했어. 그래서 외부 API 호출이 실패한 경우, 그냥 로그만 남기고 무시하도록 해뒀어. 혹시 외부 API 에러 시 이정도는 그냥 운영적으로 수동으로 처리하거나, 이후 에러가 많이 나면 운영팀에서 별도 로직 만들면 괜찮겠다고 생각했지.

     

      catch (Exception e) { … (로그 저장 등의 처리) // 마케팅 API 호출 실패는 외부 로직이므로, 메인 로직에 영향 없도록 별도 처리 하지 않음. }

    저런식으로 주석 달아둠 ㅋㅋ 지금 생각해보니 비동기나 메시징으로 처리했어도 괜찮았겠네.

     

      예외 클래스 설계 시 이름에 대해 생각해보면, 내 경우 서비스의 축약어를 이름에 넣는편이야. 이유는 간단한데, 일반적으로 개발자들이 다양한 라이브러리의 exception의 이름들을 전부 외우고 다니지 않는다는 점이지. 즉, 내부에서 만든 자체 exception인지 아닌지 판단하기 위해 그렇게 했어. 이게 일반적으로 좋은 방식인지는 잘 모르겠어. 내가 기존 봤던 책들 중에 커스텀 exception 이름 규칙은 없었거든ㅋㅋ 실제로 만든 exception 이름보면, MissingㅇㅇㅇTokenException, ExpiredㅇㅇㅇTokenException 이런거네. 아 물론 토큰 관련이라 기존 exception 그대로 써도 어색하진 않겠는데, 저렇게 한 이유는 특정 exception에 대해 프론트에서 어떻게 처리할지 로직을 내가 프론트 개발자들에게 지정해줬거든. 그래서 명시적으로 별도 exception으로 만든거야.

     

     

    chatGpt의 추가 질문

    이건 너와 더 이야기해보고 싶은 점인데:

    🔹 외부 API 실패 시 “실패를 삼켜야 할지, 드러내야 할지”는 항상 주관적인 판단인데

    너는 어떤 기준으로 삼켜도 된다고 판단해?

    예를 들어:

    • *“이건 운영으로 커버 가능하다”**는 건 정말 그렇게 되는가?
    • 혹은 “재시도 로직이 없다면 감싸서 알려주는 게 낫지 않나?” 같은 질문도 할 수 있어.

    개인적으로는,

    • 필수 정보 전송 API면 실패해도 “예외를 감싸서” 도메인에 전파해야 하고,
    • 부가적인 알림/마케팅 용도 API는 너처럼 삼켜도 괜찮다고 생각하는데

    혹시 너는 이 기준을 좀 더 정제된 형태로 갖고 있는지도 궁금해!

     

     

    내가 추가 답변

      결국 해당 외부 API 실패를 내 서비스의 메인 로직 실패로 받아들일것이냐에 달려있어. 애초에 쿠폰 발송 로직의 경우, 설계때부터 실패해도 괜찮도록 되어 있었어. 고객한테도 “마케팅 동의 시 바로 발송됩니다.”가 아니라, “ㅇㅇ일 이내로 발송됩니다.”라고 안내가 나가. 즉, 좀 에러나서 바로 발송안되도 운영적으로 풀 수 있게 되어 있는 구조야.

     

      반면에 이 프로젝트의 경우, 다양한 결제 방식으로 결제가 가능해. 이 때, 결제 관련 호출이 실패한 경우 애초에 메인 로직의 실패로 판단할수밖에 없어. 이런건 위 마케팅과 다르게, 운영적으로 이후에 처리할 수 없는 부분이야.

     

      그러니 그때그때 다르다고 할 수 있긴한데, 솔직히 이건 단순히 직관적으로 판단해도 어려운 부분은 아니라고 생각해. 애초에 설계상 실패해도 되도록 했냐의 문제겠지.

    댓글