게시글
질문&답변
설계방법론에 관한 질문드립니다.
램쥐뱅님 안녕하세요. 조영호입니다.좋은 질문 남겨 주셔서 감사합니다. 🙂 도메인 주도 설계와 강의 내용 사이의 관계답변을 드리면 강의에서 설명하고 있는 내용들은 도메인 주도 설계와 무관하게 객체지향 패러다임만을 다루고 있다고 보시면 될 것 같아요. . 엄격하게 말하면 도메인 주도 설계에서 다루고 있는 범위와는 교집합이 없다고 보셔도 무방합니다. 책임 주도 설계, 도메인 모델, GRASP 패턴을 포함해서 강의에서 다루고 있는 모든 내용들은 에릭 에반스가 저술한 도메인 주도 설계가 출판되기 전에 나왔던 개념들이며, 오히려 도메인 주도 설계와 섞으면 초점이 흐려지고 혼란스러워지게 될 여지가 있습니다.도메인 주도 설계는 특정한 프로그래밍 패러다임에 독립적입니다. 도메인 주도 설계는 도메인을 중심으로 소프트웨어의 복잡성을 극복할 수 있는 사고방식이자 우선순위를 결정할 때 참고할 수 있는 지침의 집합입니다. 도메인의 복잡성을 해결하기 위해 적용할 수 있는 패턴들의 집합이자, 패턴 언어라고 할 수 있습니다.에릭 에반스가 도메인 주도 설계라는 개념을 정리할 때 객체지향 커뮤니티로부터 가장 많은 영향을 받았기 때문에, 책의 예제 대부분이 객체지향을 기반으로 서술되어 있기는 하지만(객체지향의 계약에 의한 설계와 책임 주도 설계를 혼합해서 전술적 설계를 설명하고 있습니다), 도메인 주도 설계와 객체지향 패러다임은 겹치는 부분이 그렇게 많지는 않습니다. 강의의 내용과 도메인 주도 설계 사이에서 굳이 교집합을 찾는다면 도메인에 포커스를 둔다는 정도인데, 이 부분은 에릭 에반스가 책에서 언급했던 것처럼 객체지향 커뮤니티가 도메인 주도 설계를 영향을 미친 부분 중에 하나라고 볼 수 있습니다. 유스케이스와 강의 내용 사이의 관계유스케이스의 경우에는 다비 비에이라의 책에서도(제가 본 책은 쿼커스를 다루는 책이었는데 그 책을 말씀하시는건지 모르겠네요) 언급하기는 했지만, 실제로 유스케이스는 80년대 말에 시스템의 행위를 외부의 액터 관점에서 서술하기 위해 나온 개념입니다. 유스케이스는 이바 야콥슨이 처음 창시했고, 현대적인 유스케이스 서술 방식은 헥사고널 아키텍처를 제안한 앨리스테어 코어번이 정리했습니다. 유스케이스 다이어그램과 서술 방식은 나중에 이바 야콥슨이 표준 모델링 언어인 UML을 만드는 작업에 참여하면서 UML에 포함되게 됩니다.유스케이스가 UML의 일부이기는 하지만 사실 개념적으로는 객체지향과 독립적인 요구사항 서술 방법이라고 보시는게 좋습니다. 많은 자료들이 시스템의 행위를 설명하기 위해 유스케이스를 예로 들어서 설명하기 때문에 다비 비에이라의 책에서 말하는 유스케이스는 UML에서 말하는 유스케이스와 동일하다고 보시면 됩니다.강의에서는 유스케이스라는 용어를 '유스케이스 플로우'로 사용했는데요, 여기서 말하는 유스케이스 플로우는 유스케이스 다이어그램이나 유스케이스 디스크립션이 아니라 애플리케이션 로직이라고 보시면 됩니다. 일반적으로 유스케이스 다이어그램과 유스케이스 디스크립션은 유스케이스 플로우를 서술하기 위해 사용되기 때문에 유스케이스라는 용어를 공통으로 사용하기는 하지만 명확하게 두 개는 다르다고 보시면 될 것 같아요. CRC 카드CRC 카드는 켄트 벡과 워드 커닝험이 객체지향을 교육하기 고안한 방법인데요, 툴을 사용하지 않고 인덱스 카드 형태의 실제 종이를 사용합니다. 실제 인덱스 카드를 사용하는 이유는 카드를 이동시키면서 객체 사이의 협력을 커뮤니케이션하고 실험해 보기 위해서입니다. 몇몇 CRC 포맷을 지원하는 툴들이 있는 것으로 알고는 있는데, 다 마이너하고 제 경험이 제한적이다 보니 실무에서 실제로 사용되고 있는지까지는 알지 못합니다.개인적으로 CRC를 가장 잘 사용하는 방법은 동료들과 협력 과정을 통해 설계를 빠르게 고안하고 검토하기 위해 실제 인덱스 카드를 사용하는 것이라고 생각합니다. 답변이 됐는지 모르겠네요. 🙂
- 2
- 2
- 69
질문&답변
설계( DB or 도메인 ) 순서 질문
이초다님 안녕하세요.좋은 질문해 주셔서 감사합니다. 🙂 일단 신규 프로제트에서의 DB를 다른 팀의 다른 애플리케이션과 공유하지 않고 현재 개발 중인 애플리케이션에서만 종속적되는 경우라고 가정하겠습니다.전체적으로 보면 병렬이라고 볼 수 있는데요, 우선 전체 기능 중에서 현재 개발할 기능에 해당하는 부분에 대한 DB 테이블과 도메인 로직을 병렬로 개발하는 편입니다.여기에서 포커스는 애플리케이션의 전체 테이블을 설계하는게 아니라 현재 개발하고 있는 영역에 한정해서 설계한다는 점입니다.그리고 애플리케이션을 개발해 가면서 테이블과 도메인 로직을 오가면서 지속적으로 개선해 나가는 방식을 취합니다. 전체적인 관점에서는 병렬이지만 기능을 개발할 때는 해당 영역에 대해 DB 테이블을 먼저 설계하는 편입니다.어느 정도 필요한 데이터를 정리하는게 로직을 개발하는 입장에서 편하기 때문입니다.하지만 이렇게 설계한 테이블을 고정된 것으로 보지는 않고 로직을 구현하면서 테이블을 편의에 따라 수정합니다. 답변이 되었는지 모르겠네요. 😃 추가 질문이 있으시면 언제라도 질문 남겨주세요. 감사합니다.
- 1
- 2
- 111
질문&답변
객체의 생성책임을 객체의 개념 자체에 부여하는 경우에 대한 의견이 궁금합니다.
꼬고록님 안녕하세요.좋은 질문 남겨 주셔서 감사합니다. 🙂 클래스에 정적 메서드를 추가해서 객체를 생성하는 것은 정적 팩터리 메서드 또는 생성 메서드라도 부릅니다.생성 메서드의 역할은 생성자와 동일하게 해당 객체를 생성하는 것이지만, 메서드의 이름을 통해 좀 더 표현력있게 만들 수 있다는 장점이 있습니다.질문에서 Reservation.with(screening, user, amount)라는 생성 메서드 호출 구문은 아마도 내부에서 new Reservation(screening, user, amount)과 같은 생성자를 호출하기 때문에 역할 측면에서 보면 생성자와 동일하다고 할 수 있습니다. 반면에 GRASP 패턴에서 말하는 CREATOR 패턴은 어떤 객체가 Reservation.with(screening, user, amount)나 new Reservation(screening, user, amount)를 호출할 것인지를 결정하는 것입니다.질문에서 보면 ReservationService가 이 메서드를 호출해서 Reservation 인스턴스를 생성한다고 언급하셨기 때문에, Reservation 객체를 생성할 책임을 Reservation이 아니라 ReservationService에게 할당한 것이죠. 모든 클래스는 객체의 메모리를 할당하고 값을 초기화하기 위해 생성자를 정의합니다.인스턴스를 생성하기 위해 생성자를 추가하거나 내부에서 생성자를 호출하는 생성 메서드를 추가하는것은 객체가 자기 자신을 스스로 초기화하도록 만드는 것일 뿐, 객체를 생성하게 만드는 것은 아닙니다.객체가 초기화되기 위해서는 누군가 생성자나 생성 메서드를 호출해줘야 하죠.CREATOR 패턴에서 말하는 생성 책임은 이렇게 어떤 객체가 그 객체가 생성되도록 생성자나 생성 메서드를 호출할 것인지를 결정하는 것이라고 생각하시면 됩니다. 답변이 됐는지 모르겠네요.감사합니다. 🙂
- 1
- 2
- 127
질문&답변
6-5. 설계 평가하기 - 수업자료 클래스 오표기
제보해 주셔서 감사합니다. 🙂 우선 pdf 강의 자료 수정 후 업로드했습니다.동영상 자료는 따로 시간을 내서 오류가 있는 부분 수정 후 업로드하도록 하겠습니다.꼼꼼하게 봐주셔서 감사드리고,강의를 수강하시는데 방해가 되는 오타가 있는 점도 사과드립니다.다음 강의에는 좀 더 꼼꼼하게 살필 수 있도록 노력하겠습니다.남은 부분도 재미있게 들어주시고 즐거운 하루 보내세요! 🙂
- 1
- 2
- 89
질문&답변
도메인의 범위
dev.taeyoung닌 안녕하세요. 좋은 질문 남겨 주셔서 감사합니다. 🙂 아래에 답변 드립니다.위와 같은 방식으로 도메인을 결정한다면, 인증을 위한 access token, refresh token 등은 도메인에 포함되지 않나요? 이런 기술적 구현체들은 실제 세계에는 없는 개념인데, 관련 모델과 서비스들을 어디에 배치하는 게 좋을까요?-> 질문 전에 말씀하신 것처럼 도메인은 "소프트웨어로 구현할 요구사항의 범위"입니다. 실세계에 있는지 없는지는 중요하지 않습니다. 그 문제가 우리가 해결해야 하는 요구사항 범위에 포함된다면 도메인에 포함됩니다. 예를 들어 게임을 만든다면 게임에 등장하는 요소들은 실세계에 존재하지 않는 것들이 많지만 이들은 게임을 만들기 위해서는 우리가 구현해야 하는 대상입니다. 해당 요소가 우리가 해결할 문제 영역에 포함된다면 도메인에 포함된다고 생각하시면 됩니다. 따라서 인증 역시 소프트웨어 안에 코드로 구현되어야 하기 때문에 도메인에 포함됩니다. 아마도 문제의 범위를 정의하는 '도메인'의 개념과 코드를 배치하기 위해 사용하는 '도메인 레이어'의 개념을 혼동하신 것 같아요. 인증이라는 요구사항을 코드로 해결해야 한다면 인증은 우리가 해결해야 하는 문제이기 때문에 도메인에 포함됩니다.반대로 서비스를 이용할 때 본인인증(kcb와 같은)이 필요한 상황이라면, 실제 세계에서의 신분증 검사 같은 방식의 신원 확인을 대체하는 Verification이라는 도메인이 생성되는 건가요?-> 본인인증이 필요한 상황이라면 이것 역시 우리가 해결할 문제이기 때문에 도메인에 포함됩니다. 'Verification이라는 도메인'이라고 말씀하신 것은 이번에는 '도메인'과 '도메인 개념'을 혼동하신게 아닌가 싶어요. 신원확인이라는 요구사항을 구현해야 한다면 신원확인을 구현하기 위한 요소들은 우리의 도메인에 포함됩니다. 그 중에서 신원 확인을 위한 도메인 로직이 존재한다면 (도메인의 유형에 따라 정확한 명칭은 다르겠지만) Verification이라는 도메인 개념을 반영하는 도메인 객체가 생성되어야 할 것이고 결과적으로 그에 대응되는 도메인 클래스가 만들어질 것입니다. 그리고 이 클래스들은 도메인 레이어에 배치될 것입니다.답변이 되었는지 모르겠네요. 🙂 감사합니다.
- 2
- 2
- 158
질문&답변
단일책임원칙과 응집도
안녕하세요.응집도(Cohesion)는 변경이 발생했을 때 클래스(또는 모듈)이 함께 변경되는 정도를 의미하고 단일 책임은 원칙(Single Responsibility Principle)은 클래스의 응집도를 높이기 위해서는 클래스(또는 모듈)이 동일한 이유로 함께 변경되도록 만들어야 한다는 설계 원칙입니다.답변이 되었는지 모르겠네요. 😃
- 1
- 2
- 127
질문&답변
예제코드 github
석재현님 안녕하세요.강의 섹션 중에 예제 코드가 제공되는 강의의 경우에는 화면 아래 쪽에 있는 '수업 노트 보기'를 클릭하시면 github 주소가 표시되어 있습니다.마지막까지 다 완강하시길 빌겠습니다.감사합니다. 🙂 (사진)
- 1
- 2
- 170
질문&답변
가격 필드가 Long fee가 아닌 Money fee가 된 계기가 궁금합니다.
Jinyoung Choi님 안녕하세요.강의가 도움이 되셨다니 다행이네요.좋은 질문 남겨 주셔서 감사합니다. 🙂 Money 클래스를 사용할 때 얻을 수 있는 이점은 아래 인프런 AI 인턴의 답변을 참고해 주시면 좋을것 같아요.일반적으로 Money와 같은 클래스를 도입하는 이유는 Long과 같은 원시형 타입으로는 표현하려는 개념을 명확하게 드러낼 수 없을 때 사용합니다.예를 들어서 다음과 같은 클래스가 있다고 생각해 보겠습니다.class AnyClass { private Long amount; private Long distance; public void increase(Long amount, Long distance) { this.amount += amount; this.distance += distance; } }위 클래스에서 amount와 distance는 타입은 Long으로 동일하지만 의미는 완전히 다릅니다.하나는 금액을 의미하고 다른 하나는 거리를 의미합니다.따라서 다음과 같이 클래스를 추가해서 의미를 명확하게 코드 안에 표현하는 것이 더 좋은 방법입니다.class AnyClass { private Money amount; private Distance distance; public void increase(Money amount, Distance distance) { this.amount = amount.plus(amount); this.distance += distance.add(distance); } }원래의 클래스는 둘 다 Long 타입이기 때문에 파라미터를 잘못 넘기더라도 컴파일 타임에 체크를 하기 어렵습니다.예를 들어서 AnyClass 인스턴스에 1000원을 더하고 거리에 10km를 더하고 싶다면 아래와 같이 메서드를 호출할 것입니다.anyClass.increase(1000, 10);프로그래머가 아래와 같이 두 값을 바꿔서 보내더라도 컴파일러는 오류를 체크할 수 없습니다.anyClass.increase(10, 1000);하지만 수정 후에 아래와 같이 메서드를 호출하면 타입이 다르기 때문에 컴파일 에러가 발생하게 됩니다.따라서 더 안정적으로 코드를 작성하고 유지보수할 수 있게 됩니다.anyClass.increase(Distance.of(10), Money.wons(1000));일단 Money나 Distance와 같은 클래스가 있다면 금액이나 거리를 계산하는 코드가 중복될 경우 이 클래스드로 이동시켜 중복 코드를 제거할 수 있기 때문에 재사용성도 높아집니다. 개념을 명확하게 표현하고 싶을때마다 이러 작은 클래스를 만드시면 됩니다.이런 작은 클래스들을 값 객체라고 부릅니다. 값 객체에 대한 더 자세한 내용은 아래 블로그를 참고해 주시면 감사하겠습니다.https://eternity-object.tistory.com/2 보시면서 궁금한 내용이 있으면 언제라도 질문 주세요. 🙂 감사합니다.
- 3
- 2
- 162
질문&답변
할인 조건의 구현에 대해
qwerty143님 안녕하세요. 강의가 도움이 되신다니 다행이네요. 🙂 좋은 질문 남겨주셔서 감사합니다. Screening의 isSequece 메서드와 SequenceCondition의 isSatisfiedBy 메서드는 책임의 관점에서 의미가 다릅니다. Screening의 isSequence 메서드는 회차가 동일한지를 판단합니다. Screening의 입장에서 isSequence 메서드는 할인 여부와는 상관이 없습니다. SequenceCondition의 isSatisfiedBy 메서드는 할인을 적용할 수 있는지 여부를 판단합니다. 회차가 동일할 경우 할인을 제공하기 때문에 Screening의 isSequnce 메서드를 재사용하고 있을 뿐입니다. 이런 의미적인 차이는 회차를 이용한 할인 여부 로직을 수정할 때 좀 더 명확해 지는데요만약 특정한 요일의 특정 회차에만 할인을 적용해야 한다면 다음과 같이 SequenceCondition의 isSatisfiedBy 메서드를 수정하게 될것입니다.아래 코드를 Screening에 구현하게 되면 Screening의 응집도가 낮아지게 됩니다.public class SequenceCondition implements DiscountCondition { private int sequence; private DayOfWeek dayOfWeek; @Override public boolean isSatisfiedBy(Screening screening) { return screening.isSequence(sequence) && screening.getStartTime().getDayOfWeek().equals(dayOfWeek); } } screening.getStartTime().getDayOfWeek(dayOfWeek)처럼 객체의 내부 속성을 연쇄적으로 가져오면 디미터 법칙(Law of Demeter)을 위반하기 때문에 객체의 인터페이스가 너무 복잡해지지 않는다면 screening.isScreenedOnDayOfWeek(dayOfWeek)과 같이 수정하는 것이 더 좋은 방법이기는 합니다. 답변이 되었는지 모르겠네요. 🙂
- 2
- 2
- 125
질문&답변
generic 패키지 money 클래스 관련 질문
황설탕님 안녕하세요.좋은 질문해 주셔서 감사합니다.말씀하신 것처럼 강의 내용을 확실하게 이해하는 가장 좋은 방법은 실제로 코드를 작성하는데 적용해 보는 것 같아요. :)Money 클래스가 있는 generic 패키지는 보통 어떤 특성을 가지는 클래스들을 모아놓는지 궁금합니다.개인적으로 특정한 도메인이나 애플리케이션에 종속되지 않고 여러 애플리케이션에서 재사용할 수 있는 클래스들을 모아 놓습니다.Money 클래스는 금액이 필요한 어떤 애플리케이션에서도 재사용가능하기 때문에 generic 패키지에 위치시켰습니다. Money 클래스와 같은 역할을 하는 객체들 또한 행동을 정의한 후 객체를 선택하라의 원칙에 의해 행동을 정의 한 후 필드를 결정하는 것인지 궁금합니다.Money와 같은 유형의 객체는 일반적으로 클래스의 코드를 리팩터링하는 과정에서 함께 사용되는 필드들과 관련된 로직을 명시적인 개념으로 묶기 위해 사용됩니다.따라서 행동을 정의한 후 객체를 선택하는 방식과는 조금 다른 관점에서 설계가 진행됩니다.필드가 아니라 로직을 담는 클래스를 추가한다는 점에서 행동에 집중한다는 점은 동일하지만 협력을 설계하는 과정에서 행동을 먼저 결정하고 객체를 나중에 결정한다기 보자는 이미 구현된 로직을 옮겨 담을 객체를 추가한다는 개념으로 접근하는게 일반적입니다. Money 같은 성격의 클래스는 어떤 서비스를 설계하기 전 전 미리 작성한 후 서비스 설계를 해야하나요?2번 질문에서 답변드린 것처럼 일반적으로 기존 코드를 리팩터링하는 과정에서 Money와 같은 유형의 객체가 추가됩니다.물론 Money처럼 여러 도메인에 걸쳐 사용될 수 있는 클래스라면 이미 구현되어 있는 클래스를 재사용하기도 하겠지만 도메인에 특화된 객체나 존재하지 않는 경우에는 구현을 하면서 식별되는 것이 일반적입니다. Money 는 새 불변 객체를 만들어 리턴해주는 방식으로 사용하던데 Screening 등의 클래스에서는 불변 객체를 사용하지 않는 이유가 있는지, 있다면 특정 클래스를 불변 객체로 설계하는 기준이 있는지 궁급합니다.Money는 값 객체(Value Object)에 속하고 Screening은 참조 객체(Reference Object)에 속합니다.값 객체는 참조 객체의 속성을 표현하기 위해 사용되며 불변으로 만드는 것이 일반적입니다.값 객체와 참조 객체의 개념과 용도에 대해서는 제 블로그에 있는 시리즈 글을 참고하시면 쉽게 이해하실 수 있으실 거에요. :)https://eternity-object.tistory.com/2답변이 되었는지 모르겠네요. 🙂 감사합니다.
- 1
- 2
- 107