호주에 살고 있는 소프트웨어 개발자입니다. 30년간 다양한 분야의 시스템과 서비스를 개발해본 경험이 있습니다.
스프링 프레임워크와 관련 기술을 좋아하고 JVM 기반 언어를 주로 사용합니다.
한국스프링사용자모임(KSUG)을 설립하고 활동했고, 토비의 스프링이라는 책을 쓰기도 했습니다.
개발과 관련된 다양한 주제에 관해 이야기하는 것을 좋아합니다.
Khóa học
Đánh giá khóa học
junhyun1993
·
[Meetup VOD] Nhà phát triển 31 năm kinh nghiệm chia sẻ "Thời đại AI, cách sống như một lập trình viên"[Meetup VOD] Nhà phát triển 31 năm kinh nghiệm chia sẻ "Thời đại AI, cách sống như một lập trình viên"- Toby's Clean Spring - Mô hình Domain Model và Kiến trúc Hexagonal Part 1
blessthy
·
[Meetup VOD] Nhà phát triển 31 năm kinh nghiệm chia sẻ "Thời đại AI, cách sống như một lập trình viên"[Meetup VOD] Nhà phát triển 31 năm kinh nghiệm chia sẻ "Thời đại AI, cách sống như một lập trình viên"me
·
[Meetup VOD] Nhà phát triển 31 năm kinh nghiệm chia sẻ "Thời đại AI, cách sống như một lập trình viên"[Meetup VOD] Nhà phát triển 31 năm kinh nghiệm chia sẻ "Thời đại AI, cách sống như một lập trình viên"
Bài viết
Hỏi & Đáp
Domain Expert가 정확히 어떤 역할을 하는 사람인가요?
안녕하세요.예전에는 소프트웨어를 개발해서 적용하는 업무 영역과 대상, 즉 도메인이 시스템을 개발하기 이전에 이미 존재했고, 비즈니스가 동작하던 경우가 대부분이었죠. 그 시절에는 보통 IT 부서 외의 실무 업무와 구조, 비즈니스를 잘 알고 있는 사람을 도메인 전문가라고 불렀습니다. 그런데 인터넷 기반의 비즈니스들이 등장하고 발전하면서 이제 소프트웨어 개발자들도 비즈니스에 대해서 처음부터 이해하고, 여러 업무와 의사 결정에 참여하기도 하면서 회사에서 일을 오래 해온 개발자가 도메인 전문가로서 충분한 지식을 가지고 있는 경우도 많이 있죠. 그 분들이 도메인 전문가로서의 역할도 해낼 수 있습니다. 아직 시작한지 얼마 안 된 스타트업의 경우도 마찬가지죠.그런데 도메인 중심의 소프트웨어 개발팀에서 도메인 전문가는 단순히 업무에 대한 지식과 경험이 많기만 해서는 안 됩니다. 도메인에 익숙하지 않은 개발자들과 적극적으로 소통하면서 자신의 경험과 지식을 도메인 모델로 만들어가는데 적극적으로 참여하고, 개발 기간 내내 꾸준한 협업을 지속할 수 있어야 합니다. 또, 보편 언어를 개발자들과 함께 "개발"해나가는 의지도 중요하죠. 원래 현장에서 쓰이던 언어를 그대로 가져오는 것이 보편 언어는 아닙니다. 도메인 모델을 만들고 발전시켜 가면서 모델에 사용되는 언어를 기존에 현업에서 정확하지 않은 의미로 사용해오던 것, 또는 기존에 없던 언어를 새롭게 개발할 수도 있어야 합니다. 그런 작업에도 적극적으로 참여할 수 있어야겠죠. 때로는 개발자들이 좋은 아이디어를 제시하고, 그게 도메인 모델의 보편언어가 되어 도메인 전문가들이 그걸 수용해서 소통하게 되기도 합니다. 결론적으로 질문하신 내용이 맞습니다. 온라인 서비스라면 도메인 자체가 기술로 만들어진 서비스 또는 시스템이기 때문에 그에 대한 이해를 가장 잘 하고 있고, 이걸 기반으로 개발자들과 소통하면서 도메인 모델을 발전시켜나가는 사람이라고 보시면 됩니다. 그런데 규모가 커지면 한 명이 모든 도메인의 영역을 다 자세히 알지 못하는 경우가 있습니다. 또, 현장에서는 업무 부서마다 유사하지만 다른 용어를 쓰기도 하고, 언어가 혼란스러워 지기도 합니다. 이 정도가 되면 좀 복잡한 전략이 필요할 때도 있습니다. 이걸 DDD에서는 전략 패턴이라고 말합니다. 실제로 규모가 큰 조직인 경우 도메인 모델은 하나 이상이 되고, 보편 언어도 그에 따라 여러개가 되기도 합니다. 이건 좀 고급 주제이긴한데, 어쨌든 그런 경우에도 도메인 전문가가 참여하는 것이 필요하겠죠.
- 0
- 1
- 24
Hỏi & Đáp
28. 회원 애플리케이션... / 이메일 중복 검사를 도메인 서비스로 수행하는 방식에 대해서
말씀하신 대로 회원의 이메일은 중복되지 않는다는 도메인 지식을 인터페이스로 만들어 도메인 서비스로 정의하고, 메소드 호출 주입 방식을 써서 필요할 때 엔티티에 전달해서 생성 로직 과정중 검증 기능으 사용하게 만들 수 있습니다.이런 경우 도메인 규칙이 도메인 모델을 직접 반영한 코드에 더 명시적으로 드러나고 풍부한 모델로서 가치가 있습니다. 다만, 이번 강의에서는 제가 이메일 중복을 체크하는 것은 같은 도메인 로직을 담당하지만 도메인 계증 내부가 아닌 애플리케이션 서비스에 두는 방식을 선택했습니다. 우선 외부 자원을 이용하는 시스템에 의존해야 하고, 애플리케이션이 포트를 통해 제공하는 기능의 흐름에 절차적으로 들어가기에 적당하다고 생각을 했고요. 다른 도메인 로직에 비해 단순 조회를 이용한 검증 로직이라서 도메인 서비스까지 동원해서 작성할 필요가 있을까라고 생각을 했습니다. 물론 리포지토리를 이용해서 동작하는 기능이 내부에 들어갈 수 있다고 해서 도메인 서비스로 만들면 안 되는 것은 아닙니다. 복잡한 도메인 규칙, 예를 들어 포인트 정책이나 결제 한도 계산 등의 도메인 로직은 구현이 자주 바뀌고 다양한 외부 리소스에 의존할 가능성이 높지만, 이건 도메인 모델로 표현되는 핵심적인 기능이기 때문에 이런 경우엔 도메인 서비스로 만들어 사용할 것입니다. 이때 도메인 모델 내부에서 동작하는 코드가 런타임시에 외부 리소스까지 연결되기 때문에 발생하는 복잡성 보다 중요한 핵심 도메인 로직을 처리하는 코드이기 때문에 도메인 모델 내에 두고, 개발팀 내에서 소통하고 테스트로 검증하는 등의 작업이 더 가치를 줄 수 있기 때문에 그런 선택을 하게 되는 것이죠.제가 이번엔 그런 선택을 했지만, 만약 개발팀이 아직 도메인 서비스를 작성하는 것에 익숙하지 않고, 메소드 주입을 이용해서 도메인 서비스를 사용하면서 그 구현은 도메인 외부에 작성하는 방식의 접근 방법을 한번 손쉽게 적용하는 연습을 해본다면, 그때는 이메일 중복 체크를 위한 도메인 서비스를 따로 만들어 사용했을 것입니다. 이 접근 방법도 꽤 여러가지가 있습니다. 이어지는 강의에서 이에 대해서 여러가지 접근 방법을 한번 보여드리도록 할게요.
- 0
- 1
- 16
Hỏi & Đáp
회원 애플리케이션 서비스 테스트 (1)
앗. 제가 코딩 영상을 편집하다가 getTos() 추가하는 부분을 잘못 삭제한 듯합니다. 14분 36초 부분을 보시면 getTos() 메소드가 있는 걸 보실 수 있습니다. 당연히 메소드가 있어야겠죠.(사진)
- 0
- 2
- 25
Hỏi & Đáp
정적 팩토리 메서드 관련 질문드립니다!
네. 보통은 생성자를 통해서 한번에 인스턴스를 만드는 것이 완결성이 있어서 좋다고 여겨집니다. 저도 동의하고요.그런데 여기서 정적 팩토리 메소드를 사용하려고 했던 이유 중에는 생성자 파라미터가 길어지고 같은 타입의 파라미터가 연속으로 나올 때 순서가 잘못 되어서 버그가 생기는 문제를 피하기 위함도 있었습니다. 그런데 팩토리 메소드에서도 생성자를 호출하는 방식을 쓴다면 그 문제는 여전히 남아있겠죠.그래서 여기서는 마치 빌더패턴을 사용하듯이 하나씩 프로퍼티를 설정하는 방식을 사용해봤습니다. 팩토리 메소드도 일종의 생성자라고 생각한다면 그 안에서 완결성을 가지는 오브젝트를 초기화하는 코드를 넣는 것도 나쁘지 않겠다고 생각합니다. 물론 저도 익숙한 방법은 아니긴합니다. 생성자를 private으로 만들고, 팩토리 메소드를 단일 오브젝트 생성 구조로 만드는 경우라면 이 안에서 초기화와 검증까지 모두 완료할 수 있습니다. 더더욱 필드 이름과 파라미터로 전달된 오브젝트에서 꺼내는 값의 이름을 일치시키면서 코딩을 할 수 있기 때문에 나중에 생성 시점에 전달할 파라미터가 변경되거나 순서가 바뀔 때 실수할 확률이 줄어듭니다.final은 어짜피 JPA 엔티티이고 필드 액세스를 디폴트로 적용했기 때문에 쓸 수 없습니다. JPA 엔티티는 가변 객체로 선언해야 합니다. 사실 생성자를 써도 파라미터로 넘어온 값을 모두 필드에 할당하지 않을 수도 있습니다. 그러면 부분 초기화 문제가 발생하고요. 검증 누락도 마찬가지죠. 어디선가는 생성 로직을 완벽하게 구성해야 할 책임이 있습니다. 그걸 팩토리 메소드로 가져오는 것도 하나의 옵션으로 생각하시면 좋겠습니다. 물론 저도 생성자만 쓰기도 하고, 프로퍼티 갯수가 많지 않다면 생성자에 파라미터로 풀어서 전달하기도 합니다.
- 0
- 2
- 33
Hỏi & Đáp
백오피스 개발에도 헥사고날 아키텍처가 유용할까요?
우선 헥사고날 아키텍처를 사용하는 것이 도메인 모델 패턴을 사용하거나 DDD 스타일의 패턴을 적용해야 하는 것은 아닙니다. 강의에서는 이 두가지를 결합해서 적용했지만, 순수하게 헥사고날 아키텍처 패턴만 따라서 개발하더라도 유익이 많습니다. 반복적인 CRUD가 많이 등장하는 데이터 처리 중심의 애플리케이션이더라도, 20%쯤에 해당하는 간단하지 않은 로직을 처리하는 코드는 테스트가 잘 작성되는 것이 중요할 겁니다. 백오피스라면 더더욱 버그 없이 작성된 코드를 만드는 것이 중요하고 그런 면에서 헥사고날 스타일로 작성해서 충실한 테스트를 만드는 것이 주는 장점이 많습니다. 그런데 요즘 스프링을 이용한 개발을 충실하게 하시면 사실 대부분 헥사고날 아키텍처에 거의 가깝게 개발하고 계실 겁니다. 스프링 데이터 JPA를 쓴다면 더더욱 그렇고요. 그렇다면 계층형 스타일과 다른 점은 크지 않습니다. 중요한 것은 서비스에 인터페이스를 반드시 정의하고, 어떤 의도로 어떤 기능을 사용하는지를 잘 담아두고요. 서비스 계층의 비즈니스 로직이 외부로 노출되지 않도록 주의하면 됩니다. 그리고 리포지토리를 비롯해 서비스에서 외부 시스템을 사용하는 코드도 인터페이스를 충실하게 작성하고 이를 서비스 모듈 안에 정의해두고, 어떤 환경에서는지 테스트하기 좋게 만들어서 중요하다고 생각되는 작업에는 반드시 테스트를 작성하시면 됩니다.헥사고날 아키텍처 자체는 그리 어려운 것이 아니고, 스프링에서는 계층형 아키텍처와 구조적으로 거의 차이가 없습니다. 강의에서 강조드린 헥사고날 아키텍처의 특징과 요구사항이 어떤 것인지만 잘 생각해보시고 그걸 따를 수 있도록 하시면 좋겠습니다.CRUD에 대해서 다 테스트를 만들어야 할지도 고민이 될 수 있는데, 매번 동일한 형식의 구조이고 별다른 로직이 없다면 처음부터 안 만드셔도 괜찮습니다. 그보다는 상태 변화와 복잡한 조회, 여러 엔티티가 한번에 바뀌는 로직 등에 대해서만 테스트를 잘 작성해보세요. 도메인 모델 패턴은 헥사고날 아키텍처가 요구하는 것은 아닙니다. CRUD 중심이라면 도메인 모델 패턴을 쓴다고 해도 아주 단순한 구조가 되겠죠. 하지만 20%에 해당하는 로직을 처리하는 코드에서 엔티티가 가진 정보를 이용하는 코드는 엔티티 내부에 작성하는 정도만 충실하게 하셔도 충분합니다. 제 생각에는 헥사고날 아키텍처가 오버엔지니어링이 될 가능성은 거의 없습니다. 시중에 잘못 알려진 복잡한 형식을 요구하는 오해를 가진 잘못된 헥사고날 아키텍처를 따르지만 않는다면요.
- 0
- 2
- 58
Hỏi & Đáp
spotbug + @NonNullApi 로만 Null 방어가 될까요?
안녕하세요.@NonNullApi와 이를 체크하는 SpotBug과 같은 도구는 빌드 과정에서 명시적으로 해당 체크를 요구할 때만 검증을 해줍니다. 만약 SpotBug 체크를 빌드에 추가하지 않으면, 자바 언어의 기본 빌드에서는 이 애노테이션은 아무런 역할을 해주지 못하겠죠. 그래서 직접 null 체크하는 코드를 requireNonNull()을 넣은 것입니다.Null 값을 잘못 전달하는 상황을 막아주는 것은 런타임에 동작하는 requireNonNull() 입니다. 이게 가장 중요하죠. 자칫 null 값이 DB에 들어가게 되면 이후 여러가지 복잡한 버그가 발생하고 데이터가 망가질 겁니다. 이걸 복구하는 건 꽤나 어려운 작업입니다. 하지만 이렇게 런타임에 안전장치를 해두는 것은 실제 해당 코드가 실행되기 전에는 문제가 있다는 것을 알 수가 없습니다. 서버를 배포하고 2-3일 지나서 처음으로 requireNonNull() 코드가 실행이 된다면, 그때까지 코드의 결함이 있었음에도 확인을 못하고 방치하게 되는 거죠.그래서 100% 완벽하게 null 문제를 발견해주지는 못하지만(왜냐하면 API 등을 통해서 외부에서 전달되는 값에 의한 null 문제나, 우리가 작상한 코드 외의 라이브러리나 다른 모듈을 통해서 잘못 사용되는 경우도 있으니까요) 그래도 검토 가능한 코드 레벨에서 명백하게 null 값이 들어갈 수 있는 오류가 있다면 미리 체크할 수 있게 하기 위해서 SpotBug 같은 빌드 시점에 동작하는 정적인 체크 기능을 사용해서 결함을 일부라도 미리 발견할 수 있도록 한 것입니다. 이런 정적인 코드 분석을 통한 결함 사전 발견은 IntelliJ 같은 IDE에서도 일부 제공됩니다. 이것도 역시 100% 문제를 다 체크해주지 못하기 때문에 우리가 코드에서 항상 주어진 값을 검증하는 코드를 넣어서 실행시점에서라도 확인이 가능하도록 만드는 것이 필요합니다.
- 0
- 2
- 30
Hỏi & Đáp
domain 모듈에 entity를 정의한다고 했을때
질문하신 내용에 대해서는 강의 후반부에 자세하게 설명드렸습니다.그런데 JPA와 MongoDB를 동시에 써서 개발하는 경우가 아니라면 어떤 방식으로 접근하든(도메인 엔티티와 영속 엔티티를 분리하고 매핑, 또는 단일 엔티티 유지) 코드의 변경은 필요합니다. 동시에 쓰는 경우는 별로 없다고 생각하고요.당장 JPA와 같은 RDB에서는 요즘 Long 타입의 인조키를 선호합니다. 반면 MongoDB는 String 타입의 id를 사용하죠. 그런식의 코드 변화는 도메인 엔티티에도 영향을 미칩니다. 모든 엔티티는 고유한 ID를 가지고 있어야 하니 그 값을 매핑 받아야 하기 때문이죠. 더 나아가 그런 DB의 변화가 있다면 마이그레이션 작업도 크게 일어날 것입니다. 영속 엔티티를 분리해서 작성하는 번거로운 작업을 굳이 해야할 필요가 있는 경우는, 아마도 레거시 DB를 사용하거나, 서로 영향을 주기 힘든 다른 팀과 DB를 공유해서 우리가 DB 설계와 생성 부터 컨트롤 할 수 없고, 도메인 설계를 해보니 매핑이 바로 안 돼서 매번 번거로운 전환이 필요한 경우입니다.그 외에는 도메인 엔티티를 영속 저장용 엔티티로 활용하는 것이 대체로 더 나은 선택일 것입니다.
- 0
- 2
- 29
Hỏi & Đáp
서비스 단위 테스트 코드 작성
단위 테스트는 말 그대로 한 단위를 어떻게 정의하냐에 따라서 여러가지 접근 방법이 있습니다. 단위 테스트라고 무조건 클래스 하나만 테스트할 필요는 없습니다. 그리고 단위 테스트라고 하더라도 인터페이스를 타고 테스트하는 것이 좋고요.애플리케이션 서비스 계층은 외부 리소스, DB 등에 의존하는 특징이 있습니다. 이걸 단위 테스트로 하려면 의존 대상에 대한 적절한 Mock, Stub 등을 만들어서 해야 합니다. 이번 강의에서 테스트를 만드는 여러가지 방식에 대해서 설명드리면서 단위 테스트를 작성하는 방법도 설명 드렸습니다. 그 부분을 참조해보시면 좋겠습니다. 아니면 다른 구체적인 케이스가 있다면 알려주세요.
- 0
- 2
- 39
Hỏi & Đáp
혹시 다음 편은 언제쯤 오픈할까요?
11월 중에 마무리하는 것을 목표로 열심히 진행하고 있습니다.
- 0
- 2
- 59
Hỏi & Đáp
required 포트에 관해서
리포지토리처럼 DIP가 적용된 경우에 이 인터페이스는 이를 사용하는 쪽 모듈에 가까이 두는 것이 중요합니다. 여기서는 헥사곤 내부에 두었지요. 그런데 그 중에서 여러 모듈에서 사용하게 되는 것들이 있습니다. 외부 연동용 API도 그렇고요. 보안쪽도 그럴 수 있죠. 또 조회용 리포지토리도 많은 경우 그에 해당됩니다.이 때 두가지 접근방법을 둘 수 있습니다. 여러 모듈에서 사용되는 required interface에 해당되는 리포지토리를 shared 모듈을 별도로 만들어서 그 안에 두는 방법입니다. 이러면 어떤 모듈에서든 자유롭게 사용해도 되고, 의존 방향도 단방향으로 적절하게 구성할 수 있습니다. 다만, 도메인의 엔티티가 파라미터나 리턴 값으로 사용되고 있다는 점에서 리포지토리를 메인 엔티티가 존재하는 모듈 외부로 가져오는 것이 적절한지는 의문이기도 합니다. 그래서 두번째 방법으로 헥사고날 애플리케이션 모듈의 의존 방향이 꼬이지 않는다면, 즉 양방향 참조가 일어나지 않는다면 다른 모듈의 리포지토리는 필요에 따라서 사용하는 것을 허용하는 것입니다. 이때는 반드시 조회 기능에 제한을 두어야 합니다. 다른 모듈에서 수정까지 일으키면 의존관계가 얽혀서 나중에 이해하기 힘든 코드가 되버리겠죠.Team -> Member 의존 구조로 정의한다면 Team 애플리케이션 쪽에서는 Member의 provided interface를 태우지 않고, 바로 MemberRepository(여기서는 Finder라고 하셨네요)에 접근하는 것까지 허용하는 것입니다. 하지만 반대는 만들지 않는 것이 좋습니다. Member에서 Team에 접근해야 하는 상황이라면 도메인쪽 설계를 다시 고민해보거나, 여기서 모듈간에 DIP를 다시 쓰는 방법이 있습니다.
- 0
- 2
- 26





