우려했던 일이 벌어졌습니다…
할인정책을 바꾸라고 하시네요?!
기어코 다 만들어버리니까 바꾸라는 사장님. 하지만! 저희는 여기서 사직서 대신 키보드와 마우스를 들고 객체지향을 통해 간단하게 수정을 진행해 볼까 합니다.
가보죠!
새 할인정책 만들기
바뀐 할인정책에 대해 들어볼까요?
사장님: 생각해보니까 1,000원으로 고정 할인하면 낱개별로 라면 하나씩 계속 주문하면 계속 무료인거잖아?? 산 금액에서 10%할인해주는걸로 당장 바꿔!!
이제 주문한 금액의 10% 할인으로 바꿔줘야겠네요. 우리의 프로젝트를 열어봅시다. 그리고 우리의 discout패키지에 RateDiscountPolicy라는 정률 할인정책을 만들어봅시다~
그리고 FixDiscountPolicy와 비슷하게 dliscount함수를 VIP라면 가격의 10%를 return해주는 함수로 override해줍시다!
코드 이해되시죠? 어려운건 언제든 댓글로 질문해주세요~~
좋아요! 아주 간단하게 정률할인정책을 만들었어요! 이제 적용만 시키면 되겠어요! 다음장에 적용해볼게요.
새로 만든 할인정책 적용하기
이제 우리가 FixDiscountPolicy로 적용했던 코드들을 싹 찾아서 RateDiscountPolicy로 바꾸기만 하면 완료됩니다. 근데 혹시.. 어느 파일에서 해당 할인정책을 선언하였는지 다 기억나시나요??
일단 다행히도 우리는 만든 코드가 그렇게 많지 않다보니 하나씩 뒤져보면 알 수 있겠어요! 뒤져보니 저희는 OrderServiceImpl에 FixDiscountPolicy를 선언했더라구요?
(여기 한곳만 있어서 다행이지…)
선언한게 한개가 아니고 여러개가 있었다 하면 찾아서 수정하는것만해도 큰일이겠어요.. 우리 코드는 아직 객체지향적이지 않다는 증거가 되겠네요? 일단은 해당 문제는 조금이따가 보고 우선 FixDiscountPolicy()를 RateDiscountPolicy()로 바꿔줍시다!
좋아요!! 요거 하나 바꾼것으로 인해서 우리의 주문 서비스는 이제 할인 정책이 싹 바뀌었습니다! 정말 그런지 볼까요??
지난 글에 과제로 드린 Order 테스트 코드로 가볼까요?
OrderServiceTest
우리는 전에 무조건 VIP면 1000원 할인이었기 때문에 할인될 금액이 1000원이면 테스트가 성공되도록 만들었습니다.
하지만 이제 order 서비스의 할인정책을 바꾼 후 현재 테스트코드는 아무런 수정을 하지 않은 채 테스트를 실행하면 어떻게될까요?
결과화면
(실패!)
이전 할인정책이었으면 초록 체크 멋있게 빡 떠야했을 테스트였지만 실패해버렸네요? 여러분도 당연히 그렇겠다시피 이제는 10%를 할인해주기때문에 24900원의 10%인 2490원이 할인금액으로 나와줘야하기 때문이죠.
그렇다면 이 테스트가 성공하기 위해선 어떻게 해야할지 감이오시죠?
상품가격에서 10%의 금액이 할인금액으로 나올것으로 기대하고 수정해주시면 됩니다! 2490으로 금액을 바꿔주면?
결과화면
스무스하게 성공하게됩니다~~
객체지향에 한걸음 더 다가가기
우리가 위 과정을 진행하는데 뭔가 영 찜찜한게 하나 있었죠?
우리가 할인정책을 선언한 코드가 하나가 있어서 다행이지 그게 여기저기 이미 많이 쓰고있는 상태에서 갑자기 정률할인정책으로 바꿔야했다면 참 난감했을 것 같아요.
우리의 프로젝트가 더 커지기 전에 어서 이에 대해 조치를 취해봅시다! 우리는 이제부터 영화감독처럼 캐스팅 목록을 만들겁니다!
갑자기 뜬금없이 캐스팅 목록이라니 뭔가 싶으실겁니다. 우리가 영화를 찍는다고 쳐볼게요. 제가 어제 “서울의 봄”이란 영화를 너무 재미있게봐서 우리는 이제 서울의 봄 감독이 된거라고 생각해봅시다. 영화는 많은 스태프들과 같이 찍게 돼요. 카메라, 조명, 음향, 분장 등등 여러 팀들이 있을겁니다.
일단은 감독인 제가 여기저기 돌아다니면서 배우를 캐스팅해요. 황정민씨한테 가서 전두환광 역을 해달라고 싹싹 빌어서 캐스팅이 됐어요! 그리고 정우성씨한테 이태신역을 해달라고 부탁드려서 정우성씨도 캐스팅이 되었어요.
(너무 재밌었어요 꼭 보세요!!)
그 이외 여러 배우들이 있긴하겠지만 간단하게 두명만 봐볼게요!
영화는 영화의 시작부터 끝까지 황정민 배우가 전두광 역할을 하게 되겠죠? 그래서 저는 이제 황정민 배우한테 분장 시작하라고 분장팀에게 이렇게 외쳤습니다.
분장팀! 지금 전두광역 배우 대머리 분장 해주세요!
근데 분장팀은 전두광역 배우가 누구인지 모르겠답니다. 그래서 일단 알려줬어요. “아~ 황정민씨가 전두광역 배우예요!”
그 이후 전두광의 머리가 더 반짝반짝 빛났으면 좋겠다 싶은 저는 또 이렇게 외쳤습니다.
조명팀! 전두광역 배우 머리쪽에 조명 세게 줘서 더 머리 빛나게 해주세요!
근데 또 조명팀도 전두광역 배우가 누군지 모르겠다고 하네요ㅠ 저는 했던말 또 해야겠죠? “아~ 황정민씨가 전두광역 배우예요!”
거기다 갑자기 중간에 정우성씨가 하차하게되어서 영화 중반부터 다른 배우가 이태신역을 하게 되었다 가정할게요. 그럼 모든 팀들한테 하나하나씩 가서 이제 배우 또 누구누구로 바뀌었어요 그때그때 얘기해줘야 할거예요.
불편하죠? 이게 바로 현재 저희 프로젝트의 상황입니다.
인터페이스의 구현체 하나하나 사용할 때 마다 어떤 구현체를 썼는지 각각 명시해주고 있는 상황입니다.(무슨역 배우가 누구인지 얘기해줘야하는 상황) 거기다 다른 구현체로 바꿀때 마다 이전 구현체를 쓰고있는 곳을 전부 찾아가서 새 구현체로 바꿔줘야하죠?(배우가 바뀌면 바뀌었다고 다 얘기해줘야하는 상황)
감독인 저는 머리를 쓰기 시작했습니다. 모든 팀에게 태블릿을 하나씩 나눠주기 시작했어요. 그리고 제가 처음에 말했던 캐스팅 목록을 공유 엑셀 파일로 넣어서 전달해준것입니다. 해당 엑셀 파일은 감독인 저만 수정할 수 있고, 제가 수정할 때 마다 해당 엑셀파일을 가지고있는 모든 사람들의 엑셀파일도 같이 수정되는 기능을 갖고있어요.
그럼 캐스팅 목록.xlsx에 어떤 내용이 들어있는지 확인해봅시다.
캐스팅 목록
배역 | 배우 |
전두광 | 황정민 |
이태신 | 정우성 |
그럼 제가 전두광 배우 대머리분장 해달라고 요청한다면? 분장팀은 캐스팅목록.xlsx를 보고 배역이 전두광인 배우에게 가서 대머리 분장을 해 주게 되겠죠? 그리고 정우성씨가 하차를 하게 된다면 저는 정우성 배우 쪽에 정우성을 지우고 새로 들어온 배우의 이름으로 바꾸면 될겁니다. 다른 직원분들은 이 캐스팅목록이 바뀐걸보고 배우가 바뀌었다는걸 알아채고 이에 맞게 작업을 할 것입니다.
이 캐스팅목록이라는 파일 하나 만든것에 감독은 많은 수고를 덜게 되었어요. 우리 프로젝트에도 그럼 이 캐스팅목록 파일과 비슷한 역할을 하는 파일을 만들어 보도록 합시다!
설정파일의 등장
우리 프로젝트의 설정파일을 만들어 볼겁니다. 얘가 이제 캐스팅 목록과 같은 역할을 하겠죠.
이름은 AppConfig라고 지어주고 com.shopping.naver 안에 만들어줍시다!
AppConfig에선 어떤 내용이 들어가면 좋을지 한번 같이 봐봅시다.
AppConfig
이런식으로 new를 이용한 객체의 새로운 생성은 AppConfig에서만 이루어지도록 지정해주는겁니다.
코드를 보면 new MemoryMemberRepository()가 두번 나오는게 조금 불편하죠? 리팩토링까지 해줍시다!
이렇게 각각 new를 사용하는 애들을 각각 다 하나의 함수로 만들어주면 깔끔하게 리펙토링이 완료됩니다!
이제 이 설정파일을 적용시켜줘볼까요?
MemberServiceImpl에 적용
이제 MemberServiceImpl 생성자를 통해 AppConfig에서 지정한 memberReposity를 가져와서 본인의 memberRepository에 지정해주는 매커니즘이 적용되었습니다.
이렇게 되었을 때 MemberServiceImpl은 MemberRepository가 MemoryMemberRepository가 들어올 지 다른게 들어올 지 알 수가 없겠죠? 현재 코드에 Memory라는 말이 코빼기도 안보이니까요.
이게 바로 위에서 설명한 감독이 다른 팀원들한테 해당 배역의 배우를 굳이 설명해주지 않아도 되는 경우가 적용된 모습입니다!
OrderServiceImpl도 바꿔줘볼까요?
OrderServiceImpl에 적용
여기 또한 memberRepository가 메모리 형식인지 DB형식인지, 할인정책이 고정 할인정책인지 정률 할인정책인지 이 코드만 보고는 모르겠죠? 이 모습이 아주 객체지향적으로 잘 설계된 모습입니다!
이제 Test내에서 Service를 사용할 때도 AppConfig를 사용하는 방식으로 바꿔주겠습니다.
MemberServiceTest에 적용
원래 MemberService memberService = new MemberServiceImpl()로 되어있었지만 이제 AppConfig내에서 미리 필요한 요소들을 new해놓고 구성해놓은 memberService()를 가져와서 이 테스트의 memberService로 사용하게 되었습니다.
마지막으로 OrderServiceTest에도 적용해주겠습니다.
OrderServiceTest에 적용
조금 이해가 안되고 어려우실 수 있습니다. 찬찬히 생성자들과 함수들이 실행되는 과정을 따라가보면서 어떤 식으로 적용이 되는 건지 이해해보시고, 어려운 부분은 댓글로 질문해주세요~
AppConfig의 진가 맛보기
우리가 AppConfig를 사용한 이유는 고정할인, 정률할인과 같이 변경될 수 있는 클래스의 선언이 프로젝트 이곳 저곳에 되어있는 상황으로 수정이 복잡해지는것을 막기 위함이었습니다.
그로인해 이렇게 바뀌기 쉬운 이런 세부 구현체들중 우리 프로젝트에 적용되어 선택된 구현체는 어떤것인지 그리고 어느 서비스에 들어갈 것인지를 단 한 파일 AppConfig에서만 지정할 수 있도록 하였고, 우리는 변경사항이 있을 때 마다 AppConfig에 달려가 필요한 부분만 쏙쏙 수정하면 됩니다.
정말 그렇게 되었는지 봐봅시다! OrderServiceTest를 조금 바꿔줘보겠습니다.
OrderServiceTest
주문하기 테스트가 2번 진행되는데 하나는 고정할인일 때 1000원이 할인되며 성공되는 주문하기_고정할인() 함수, 그리고 또 하나는 10%의 금액이 할인금액이 되어서 성공하는 함수 주문하기_정률할인()입니다.
현재 AppConfig에서 DiscountPolicy가 어떤 할인정책으로 new되었는지 확인해보시겠어요?
위처럼 고정할인정책으로 지정되어있을겁니다. 이 상태에서 OrderServiceTest를 돌려보겠습니다.
결과화면
(고정할인으로 적용되었습니다.)
AppConfig에 지정된 고정할인정책으로 설정이 되었나보군요! 고정할인 테스트만 성공했습니다. 이제 정률할인으로 바꿔주고 싶어요. 우리는 이전에 이런 적용들을 바꿔주려면 FixDiscountPolicy가 어디에 쓰였는지 다 찾아보면서 RateDiscountPolicy로 바꿔줘야했죠? 하지만 이제는 AppConfig에서만 사용이 되기 때문에 AppConfig에서 수정만 해주면 끝입니다!
물론~ 원래 FixDiscountPolicy가 클래스 한곳에서만 쓰이긴 했지만 우리는 최악의 상황을 생각해봐야죠! 막 이클래스 저클래스 이곳저곳에 많이 쓰이고있었다 생각하면 이걸 AppConfig 한곳에서만 바꾸면 변경이 끝이 되는 상황이 엄청 반가울거예요!
AppConfig에서 DiscountPolicy를 RateDiscountPolicy로 설정해줍시다.
그리고 다시 OrderServiceTest를 실행해보시겠어요?
결과화면
어떠신가요? 간단하게 할인정책이 바뀌어버렸죠? 이것이 객체지향의 힘입니다! 프로젝트가 커져도 엄청난 수정이 찾아와도 두렵지 않게 미리 설계할 수 있는 여러분들이 된겁니다.
마치며
솔직히 오늘은 조금 어려운 내용들이 나오기도 해서 따라오기 힘드셨을거라 생각합니다. 그래도 여러번 반복해서 진행해보면서 AppConfig에 대한 이해와 구현 객체의 생성과 연결을 한 곳에서 진행했을 떄 엄청나게 다가오는 장점을 몸으로 직접 체험해보시길 바랍니다.
다음시간에는 우리가 잊고살았던 스.프.링을 드디어 써볼 예정입니다. 솔직히 스프링 강의 블로그였던거 까먹으셨죠?ㅋㅋㅋㅋ 이제 막 코드가 간결해지고 간편해지고 와 이런게 돼? 하면서 신세계를 경험하게 될 것입니다.
우리의 프로젝트에도 봄이오는 순간을 기대하며 긴 글 마치도록 하겠습니다.
감사합니다!!
해당 글에서 추가된 git commit 내용 그리고 과제 정답은 아래 링크에서 확인하실 수 있습니다.
https://github.com/KIMB0B/blog_spring/commit/d7640ecc2faea9ea098eb90ac2a92297962df1f8