Testing and Refactoring Legacy Code
-
0:02 - 0:07안녕하세요. 산드로 만쿠소 입니다.
-
0:07 - 0:10이 스크린 케스트에선
-
0:10 - 0:16테스트 코드가 없는 기존 코드에
-
0:16 - 0:18먼저 테스트를 작성하고요
-
0:18 - 0:21테스트 커버리지가 100%가 된 후에
-
0:21 - 0:24리팩토링으로 개선하는 것을 보여드리겠습니다.
-
0:24 - 0:26예제의 비즈니스 요구사항은
-
0:26 - 0:29여행자를 위한 소셜 네트워킹 서비스 같은 것입니다.
-
0:29 - 0:34콘텐츠를 보려면 로그인을 해야 하고
-
0:34 - 0:36로그인 후에
-
0:36 - 0:37다른 사람의 여행 일정을 보려면
-
0:37 - 0:40그 사람과 친구인 경우에만 조회가 가능합니다.
-
0:40 - 0:42페이스 북 처럼 말입니다.
-
0:42 - 0:46그 사람과 친구라면 그 사람의 여행 목록을 볼 수 있고
-
0:46 - 0:48친구가 아니라면 볼 수가 없습니다.
-
0:49 - 0:51이 카타에는 규칙이 몇 개 있습니다.
-
0:51 - 0:56테스트가 없는 코드는 수정을 할 수 없습니다.
-
0:57 - 1:03따라서 기존 제품 코드를 수정하려면
-
1:03 - 1:05먼저 기존 코드에 테스트 코드를 작성해야 합니다.
-
1:05 - 1:10유일한 예외는 IDE의 리팩토링 기능을 사용하는 것 입니다.
-
1:10 - 1:15절대 소스 코드에 타이핑해서 수정을 하면 안됩니다.
-
1:16 - 1:20테스트를 작성해가는 순서가 있는데요
-
1:20 - 1:24소스 코드가 이렇게 생겼다면요
-
1:24 - 1:29여기 인덴트가 깊은 부분 말고
-
1:30 - 1:34뎁스가 가장 얕은 부분부터 테스트를 작성할 것 입니다.
-
1:35 - 1:40그 후에 그 다음으로 얕은 부분의
-
1:40 - 1:41테스트를 작성하는 거지요.
-
1:41 - 1:45이런 순서로 테스트를 작성하면
-
1:46 - 1:48테스트를 작성함에 따라 제품 코드를 더 깊게 이해해 갈 수 있고
-
1:48 - 1:54테스트 스윗도 점진적으로 만들어 갈 수 있습니다.
-
1:54 - 1:58만약에 반대로
-
1:58 - 2:00가장 깊은 부분부터 테스트를 작성하려면
-
2:00 - 2:06처음부터 대상 코드를 모두 이해해야 하고
-
2:07 - 2:09많은 양의 테스트 데이터를 준비해야 합니다.
-
2:09 - 2:13그래서 코드의 얕은 부분에서 깊은 부분으로 테스트하는 것이 좋습니다.
-
2:13 - 2:15이제 코드를 볼까요
-
2:15 - 2:20TripService 클래스인데요
-
2:21 - 2:24서버 단 코드겠지요
-
2:25 - 2:27기본적으로
-
2:27 - 2:29유저의 여행 목록을 조회합니다.
-
2:29 - 2:31파라메터로 유저를 받고
-
2:31 - 2:36세션 정보에서
-
2:36 - 2:38로그인 유저 정보를 조회하고
-
2:39 - 2:45로그인하지 않았다면 예외를 던집니다.
-
2:45 - 2:51로그인 했다면
-
2:51 - 2:57로그인 유저가 파라메터로 전달된 유저와 친구인지 확인하고
-
2:57 - 3:00서로 친구라면
-
3:00 - 3:04전달된 유저의 여행 목록을 조회합니다.
-
3:04 - 3:08다 파악한 것은 아닙니다만
-
3:08 - 3:11일단 테스트를 작성해보겠습니다.
-
3:11 - 3:15코딩 하기에 편하게
-
3:15 - 3:18에디터 화면을 배치하도록 하겠습니다.
-
3:18 - 3:20저는 보통 화면을 둘로 나눠서
-
3:20 - 3:22하나는 제품 코드를 두고
-
3:22 - 3:23다른 하나는 테스트 코드를 띄웁니다.
-
3:23 - 3:27화면을 계속 스위칭할 필요가 없어서
-
3:27 - 3:31아주 편리해요.
-
3:31 - 3:34맨 처음으로 할 것은
-
3:34 - 3:36가장 얕은 부분을 찾는 겁니다.
-
3:36 - 3:37그게 어떤 코드인지는 잊어도 되요
-
3:37 - 3:39적어도 잠시 동안은요
-
3:39 - 3:42레거시 코드에 작업을 할 때
-
3:42 - 3:43맨 처음으로 할 것은요
-
3:43 - 3:45레거시 코드가 뭘 하는지 파악하려면요
-
3:45 - 3:46가장 얕은 부분을 찾는 것 입니다.
-
3:46 - 3:49Logged user 변수가 Null이 아니라면
-
3:49 - 3:51예외를 던지네요.
-
3:51 - 3:54유저가 로그인 상태가 아니면 예외를 던져요.
-
3:54 - 3:57테스트 코드로 실제로 그렇게 동작하는지 봅시다.
-
3:57 - 4:13유저가 로그인 상태가 아니면 예외를 던진다
-
4:13 - 4:17예외를 적어줍시다
-
4:17 - 4:23UserNotLoggedInException이 나와야 하는 거지요
-
4:24 - 4:27TripService 인스턴스를 만들어 봅시다
-
4:33 - 4:35먼저 Import 하고
-
4:35 - 4:37그 다음에
-
4:37 - 4:42여행 서비스에서
-
4:42 - 4:44유저의 여행 목록을 조회하고
-
4:44 - 4:47일단 유저에는 null을 넣고요
-
4:50 - 4:52지금 인피니테스트를 사용 중인데요
-
4:52 - 4:56여기 빨간색으로 결과가 표시되지요
-
4:56 - 4:59제가 코드를 저장할 때마다
-
4:59 - 5:01자동으로 필요한 테스트를 실행합니다.
-
5:01 - 5:03매번 손으로 돌릴 필요가 없어요.
-
5:03 - 5:05그런데 원래는 이게..
-
5:06 - 5:07녹색이어야 하는데요
-
5:07 - 5:10뭔가 잘못된 거에요
-
5:10 - 5:20그래서 테스트를 직접 돌리면
-
5:20 - 5:23어떻게 되어야하냐면..
-
5:23 - 5:28UserNotLoggedInException이 나와야 해요
-
5:28 - 5:31UserNotLoggedInException이 나왔어야 하는데
-
5:31 - 5:35의존 클래스 관련된 다른 종류의 예외가 생겼군요
-
5:35 - 5:38어떻게 된거냐하면요
-
5:41 - 5:47이 부분이 실행되었을 때에요
-
5:49 - 5:55유저 세션의 로그인 유저를 조회하는데요
-
5:56 - 5:58유저 세션 코드를 까서 보면
-
5:58 - 6:01물론 이 건 연습을 위한 예제 코드라서 그래요
-
6:01 - 6:03예외가 생기게 일부러 작성해놨습니다.
-
6:03 - 6:06왜냐하면 단위테스트는 다른 클래스들을 호출하면 안되니까요
-
6:06 - 6:11실수로 이 걸 실행시키면 바로 알 수 있게 하려고요.
-
6:11 - 6:15만약에 실제 Http Session을 사용하고 있다면
-
6:15 - 6:18실제 데이터 베이스를 조회하려고 할지도 몰라요
-
6:18 - 6:20단위테스트하는데 그러면 안되지요.
-
6:21 - 6:23이제 제대로 테스트하려면 이 코드를 사용하지 않게 만들어야 합니다.
-
6:24 - 6:26문제는
-
6:26 - 6:28이게 싱글톤이라
-
6:28 - 6:33Mock으로 대체할 수가 없다는 점 입니다.
-
6:33 - 6:38TripService 클래스를 손으로 수정하는 건 금지되어있으니
-
6:39 - 6:42다른 방법을 찾아야겠지요.
-
6:42 - 6:44UserSession을 Mock으로 대체할 방법을 찾아야 합니다.
-
6:44 - 6:48처음에 말한 대로 코드를 수정하는 유일한 방법은
-
6:48 - 6:51IDE의 리팩토링 기능을 이용하는 것 입니다.
-
6:51 - 6:52코드에 직접 타이핑하는 것은 안됩니다.
-
6:52 - 6:54그래서
-
6:54 - 6:56연결점(Seam)을 만들려고 합니다.
-
6:56 - 7:00연결점(Seam)은 클래스들이 이어지는 지점을 말합니다.
-
7:00 - 7:03서로 분리되는 지점이기도 하지요.
-
7:03 - 7:05이제 TripService와 UserSession 사이에
-
7:05 - 7:06연결점을 만들겠습니다.
-
7:06 - 7:09메소드를 하나 만드는데요
-
7:10 - 7:15이름은 getLoggedInUser로 하고
-
7:17 - 7:18접근제한자 protected로
-
7:19 - 7:27이렇게 TripService가 다른 클래스에 접근하는 부분을 분리해서
-
7:28 - 7:29연결점을 만들었습니다.
-
7:30 - 7:34나중에 또 손보겠지만요
-
7:34 - 7:45이제 TripService를 상속받는 TestableTripService 클래스를
-
7:45 - 7:49만들 수 있겠지요
-
7:52 - 7:54여기에서
-
7:54 - 8:04getLoggedInUser 메소드를 오버라이드해서
-
8:04 - 8:07null을 반환하게 만듭니다.
-
8:11 - 8:18이제 테스트는 TripService 대신에 TestableTripService를 사용하는 겁니다.
-
8:24 - 8:29테스트를 실행하면
-
8:29 - 8:32보시는 데로 테스트가 통과합니다.
-
8:32 - 8:35가끔 인피니테스트는 이상하게 동작할 때가 있어서
-
8:36 - 8:39믿기 어려운 경우도 있습니다.
-
8:39 - 8:45지금도 실패했다고 빨갛게 표시되었지만 실제로는 모두 정상이었지요.
-
8:45 - 8:49이걸로도 충분하겠지만
-
8:50 - 8:53테스트를 다시 돌리면.. 모두 통과합니다.
-
8:54 - 8:59코드 커버리지를 확인하면
-
9:00 - 9:06제일 얕은 부분의 코드가 실행되었는지 확인할 수 있습니다.
-
9:06 - 9:09이 것도 팁이에요.
-
9:09 - 9:11저는 레거시 코드로 작업을 할 때
-
9:11 - 9:13항상 코드 커버리지를 확인합니다.
-
9:13 - 9:15보통 코드 커버리지라고 하면
-
9:15 - 9:17테스트 커버리지라고 생각하는 경우가 많은데요
-
9:17 - 9:19저는 커버리지 숫자는 신경 쓰지 않습니다.
-
9:19 - 9:23레거시 코드에 작업을 할 때에는
-
9:23 - 9:28테스트하려는 코드가 실제로 실행되었는지가 중요합니다.
-
9:28 - 9:31긍정 오류나 부정 오류 등을 방지해서
-
9:31 - 9:33정확하게 작업할 수 있습니다.
-
9:33 - 9:35좋습니다.
-
9:35 - 9:42커버리지 표시는 지우고요.
-
9:47 - 9:50그나저나 테스트 코드가 마땅치 않네요
-
9:50 - 9:53이 테스트는요
-
9:53 - 9:56유저가 로그인 상태가 아니면 예외를 던진다
-
9:57 - 10:02이건 로그인하지 않은 유저에 대한 테스트인데요
-
10:03 - 10:05로그인하지 않은 유저는 어디를 보면 알 수 있지요?
-
10:05 - 10:08TestableTripService를 생성해서
-
10:08 - 10:11Null을 인자로 메소드를 호출해서 예외가 생기는데요.
-
10:11 - 10:15로그인 하지 않은 유저는 어디에 있을까요?
-
10:17 - 10:19여기지요. 감춰져 있습니다.
-
10:19 - 10:21그럼 유저 상태가 표현되도록
-
10:21 - 10:24수정해야겠지요.
-
10:24 - 10:27그나저나 이 부분은 조금 특이하지요?
-
10:27 - 10:30자바에서는 조금 이상한 방식이지만
-
10:30 - 10:32루비에서 종종 사용하는 방법입니다.
-
10:32 - 10:34에디터에서 코드를 이렇게 접으면
-
10:34 - 10:39이렇게 명세서처럼 표현이 됩니다.
-
10:39 - 10:40그냥 명세서인 것처럼 읽는 거지요
-
10:40 - 10:45"trip service should do something"
-
10:45 - 10:50저는 이런 방식을 좋아합니다.
-
10:50 - 10:56로그인 유저 정보가 전혀 안보이니
-
10:56 - 10:58하나 만들도록 하겠습니다.
-
10:59 - 11:06loggedInUser를 null로 만들고
-
11:08 - 11:10멤버 변수로 만든 후에
-
11:10 - 11:16로그인 유저를 조회할 때에
-
11:18 - 11:23loggedInUser 필드를 반환하게 하는 거지요.
-
11:24 - 11:29조금 더 좋아졌지요? 테스트 코드에 유저가 표현되고 있어요.
-
11:32 - 11:35음.. Null은 좀 별로네요
-
11:35 - 11:39여기 이 Null의 의미가 분명하지가 않아요.
-
11:39 - 11:51유저가 로그인하지 않았다는 건
-
11:51 - 11:55보통 웹 어플리케이션에서
-
11:57 - 12:01로그인하지 않은 유저는 게스트지요.
-
12:01 - 12:06여기에 비즈니스 개념을 적용해 볼 수 있겠네요
-
12:06 - 12:11비즈니스 인력과 대화할 때 사용하는 표현들이요.
-
12:11 - 12:14네 도메인 언어를 사용하기 시작했습니다.
-
12:15 - 12:22이 건 여행 목록을 조회할 유저지요
-
12:23 - 12:26Null 말고 더 명시적으로
-
12:28 - 12:31Unused User라고 바꾸겠습니다.
-
12:40 - 12:45어디 볼까요
-
12:46 - 12:53이 번에는 인피니테스트가 제대로 동작할까요..
-
12:54 - 12:55아니군요.
-
12:55 - 12:58안타깝게도 인피니테스트를 믿기 어렵겠네요.
-
13:02 - 13:05테스트 코드가 많이 개선되었어요.
-
13:05 - 13:08그 사이에 인피니테스트가 정상으로 돌아왔네요.
-
13:09 - 13:12말씀 드렸던 것처럼
-
13:12 - 13:15이 건 유저가 로그인하지 않았으면 예외를 던지는 테스트인데요
-
13:15 - 13:17현재 유저는 게스트
-
13:17 - 13:19그리고 예외를 던지지요
-
13:19 - 13:20좋습니다.
-
13:20 - 13:22이제 코드 커버리지를 다시 돌려보겠습니다.
-
13:22 - 13:23이제 코드 커버리지를 다시 돌려보겠습니다.
-
13:30 - 13:32그 전에 JUnit을 실행하고요
-
13:32 - 13:34그냥 확실하게 하는 거에요
-
13:34 - 13:35좋습니다.
-
13:35 - 13:37이제 코드 커버리지를 실행합니다.
-
13:43 - 13:45자 이제
-
13:45 - 13:47코드 커버리지를 보면요
-
13:47 - 13:51다음으로 얕은 부분을 쉽게 찾을 수 있습니다.
-
13:52 - 13:56이 For 반복문이겠지요
-
13:57 - 14:00친구인 경우라면..
-
14:00 - 14:02여기가 제일 깊은 부분이네요
-
14:02 - 14:06이 부분이 실행되려면 이 값이 true 이어야 하니까요
-
14:06 - 14:10그러니까 여기까지 실행되려면..
-
14:10 - 14:14유저는 로그인 된 상태인데 조회하려는 유저와는 친구가 아닌 경우입니다.
-
14:15 - 14:20그럼 이 시나리오를 검증하는 테스트를 작성하겠습니다.
-
14:20 - 14:26커버리지는 그대로 두고요
-
14:28 - 14:32테스트 이름은
-
14:34 - 14:48유저가 서로 친구가 아닌 경우에는 여행 목록을 반환하지 않는다로 하겠습니다.
-
14:48 - 14:54여기 코드를 가져와서요
-
14:57 - 15:00이제는 로그인한 유저가 필요하지요
-
15:00 - 15:04이름은 Registered User로 하고
-
15:05 - 15:07멤버 변수를 만들겠습니다.
-
15:14 - 15:24여행 목록이 필요하겠지요
-
15:24 - 15:30이름은 friendTrips로 하고
-
15:30 - 15:32import 하고
-
15:43 - 15:45이제
-
15:45 - 15:46제대로 된 유저가 필요하겠지요
-
15:46 - 15:49여기에요
-
15:49 - 15:51그러니까
-
15:52 - 15:55이름은 friend로 하고
-
15:56 - 16:00지금 친구가 필요하지요
-
16:04 - 16:11친구를 추가해주고
-
16:14 - 16:19브라질로 여행을 간다고 합시다
-
16:22 - 16:25이제 추가한 친구를 멤버 변수로 만들겠습니다.
-
16:41 - 16:45친구를 만들었지요
-
16:45 - 16:49이 친구는 브라질로 여행을 가는 또 다른 친구가 있습니다.
-
16:49 - 16:53하지만 현재 로그인한 유저와는
-
16:53 - 16:56친구가 아니네요.
-
16:56 - 17:02그래서 반환된 여행 목록은
-
17:04 - 17:09크기가 0 이어야 합니다.
-
17:11 - 17:14size() 대신에 isEmpty를 사용했어도
-
17:14 - 17:16무방하겠지요.
-
17:16 - 17:20인피니테스트는 잘 돌고 있군요
-
17:20 - 17:25확실하게 JUnit을 직접 실행하겠습니다.
-
17:25 - 17:27테스트는 통과했습니다.
-
17:27 - 17:30이미 동작하던 코드들이니
-
17:30 - 17:31당연히 테스트는 통과하겠지요.
-
17:31 - 17:33지금은 코드가 어떻게 동작하는지 알아보려고 테스트를 작성하니까요
-
17:33 - 17:35당연한 겁니다.
-
17:35 - 17:37코드 커버리지를 다시 돌려봅시다.
-
17:38 - 17:41커버리지를 보면 조금 더 진도가 나간 것을 알 수 있습니다.
-
17:41 - 17:45보시는 것처럼 조금 더 깊이 들어갔어요
-
17:45 - 17:46어떤 코드 였냐면
-
17:46 - 17:49유저가 로그인 상태이긴 해도
-
17:49 - 17:51서로 친구는 아닌 경우였지요
-
17:52 - 17:57이 부분이 항상 false를 반환했지요
-
17:57 - 17:59아직 끝난 게 아닙니다.
-
17:59 - 18:02테스트에 중복된 부분들이 있으니
-
18:02 - 18:04리팩토링을 해야 합니다.
-
18:04 - 18:06중복 코드를 없애겠습니다.
-
18:07 - 18:11@Before 메소드를 만들고요
-
18:13 - 18:15로컬 변수를
-
18:15 - 18:19멤버 변수로 바꾸겠습니다.
-
18:19 - 18:21좋습니다.
-
18:21 - 18:22좋아요.
-
18:25 - 18:27위로 옮기겠습니다.
-
18:36 - 18:37좋습니다.
-
18:38 - 18:44이제 제일 깊은 부분을 테스트하겠습니다.
-
18:44 - 18:46이 부분이 실행되려면
-
18:46 - 18:52로그인 유저가 조회 대상인 유저와 친구이고
-
18:53 - 18:56여행이 조회되는 시나리오가 필요합니다.
-
19:00 - 19:08유저가 서로 친구인 경우에 친구의 여행 목록을 반환한다.
-
19:09 - 19:11이 걸 테스트 해야 하지요.
-
19:11 - 19:14이 부분을 옮겨서...
-
19:23 - 19:25소스 코드는 절대 Copy & Paste 하지 마세요.
-
19:25 - 19:27저는 절대 그러지 않습니다.
-
19:27 - 19:29지금 본 것은 착각입니다.
-
19:29 - 19:33소스 코드는 절대 Copy & Paste 하지 마세요.
-
19:34 - 19:38더 추가해야 할 것은요
-
19:39 - 19:46로그인 유저를 친구로 추가해주고
-
19:47 - 19:49여행도 하나 더 추가합시다
-
20:03 - 20:08인피니테스트에는 테스트가 실패했다고 나오지만요
-
20:08 - 20:12유저들끼리 친구가 되었으니
-
20:12 - 20:14테스트는 통과할 겁니다.
-
20:14 - 20:15저장을 하면
-
20:15 - 20:19여전히 빨간색이네요
-
20:19 - 20:22테스트를 직접 실행하면
-
20:22 - 20:25이런 단축키를 잘 못 눌렀네요
-
20:25 - 20:28윈도우즈를 사용했었는데
-
20:28 - 20:33맥이랑 인텔리제이랑 이클립스랑 바꾸다 보니 헷갈리네요.
-
20:33 - 20:37그래도 역시 테스트가 실패합니다. 왜냐하면
-
20:38 - 20:39화면 배치는 일단 돌려놓고
-
20:40 - 20:42테스트가 여전히 실패하는 이유는
-
20:42 - 20:47이 TripDao 때문입니다.
-
20:47 - 20:49이건 Static 메소드이고
-
20:50 - 20:53메소드를 실행하면 그냥
-
20:53 - 20:55예외를 던지게 만들어놨습니다.
-
20:55 - 20:57
-
20:57 - 21:00TripDAO를 이렇게 만들어 놓은 이유는요
-
21:00 - 21:01단위 테스트에서는 이런 DAO 같은 다른 클래스들을 실행시키면 안되는데
-
21:01 - 21:07혹시 실수로 TripDAO를 실행한 경우 바로 알 수 있게 하려고 입니다.
-
21:07 - 21:13실제 시스템의 DAO는 데이터베이스에 연결해서 데이터를 조회하겠지만
-
21:13 - 21:17테스트에서는 그러면 안됩니다.
-
21:17 - 21:22이제 UserSession에 했던 것과
-
21:22 - 21:25같은 작업을 할 겁니다.
-
21:25 - 21:30싱글톤이나 Static 메소드 호출 같은
-
21:30 - 21:33강한 의존 관계를 떼어내는 거지요
-
21:33 - 21:37메소드에서 객체를 생성하는 것도 똑 같습니다.
-
21:37 - 21:39자 그러면
-
21:40 - 21:42메소드를 추가하겠습니다.
-
21:42 - 21:49이름은 tripsBy로 하고
-
21:49 - 21:51접근제한자는 protected
-
21:51 - 21:56이제 테스트 코드로 돌아가서
-
22:02 - 22:04똑 같은 작업을 하겠습니다.
-
22:17 - 22:20기본적으로
-
22:20 - 22:22여행 목록을 반환할 건데요
-
22:22 - 22:24조회할 유저를 받아서
-
22:24 - 22:27이 테스트 유저는 여행 일정을 두 개 가지고 있으니까
-
22:29 - 22:32그 걸 반환할 겁니다.
-
22:32 - 22:35the user dot trips
-
22:36 - 22:39바로 테스트 결과에 반영이 되었군요.
-
22:40 - 22:44코드 커버리지를 확인하면
-
22:44 - 22:45좋습니다.
-
22:45 - 22:51이제 기존 코드의 대부분이
-
22:51 - 22:54테스트 되었네요.
-
22:54 - 23:00기존 코드를 상속 받은 부분은 테스트가 아니라고 할 수도 있겠지만
-
23:00 - 23:02작은 연결점이니까 괜찮아요
-
23:02 - 23:05작은 연결점이니까 괜찮아요
-
23:05 - 23:07이게 최종 모습이 아니기도 하고요
-
23:07 - 23:12조금 더 정리를 해 볼까요
-
23:12 - 23:13보통은요..
-
23:13 - 23:19이 건 여기에도 있고 저기에도 있네요
-
23:19 - 23:25이런 중복 코드는 절대 좋지 않지요
-
23:25 - 23:26이 건 남겨 두고요
-
23:26 - 23:28이 건 이쪽으로 옮기겠습니다.
-
23:30 - 23:32그러면 다른 모든 테스트에 공통이 되지요
-
23:32 - 23:37로그인 된 유저가 필요한 모든 테스트에 유효합니다.
-
23:37 - 23:40그런 후에
-
23:40 - 23:42저장하고
-
23:42 - 23:44테스트를 직접 돌리면
-
23:44 - 23:46여전히 녹색이네요
-
23:46 - 23:48좋습니다.
-
23:48 - 23:52이 걸로 첫 단계가 완료되었습니다.
-
23:52 - 23:56테스트가 다 작성되었으니 이제 리팩토링을 시작할 수 있게 되었습니다.
-
23:57 - 24:02커밋하기 딱 좋은 시점이네요.
-
24:02 - 24:06git status
-
24:06 - 24:08git commit
-
24:08 - 24:18unit tests for trip service
-
24:18 - 24:20좋습니다.
-
24:24 - 24:26그런데 이 테스트를 다시 보면요
-
24:26 - 24:30레거시 코드는 종종..
-
24:30 - 24:32지금은 아주 간단한 예제를 보고 있습니다만
-
24:33 - 24:39훨씬 복잡한 메소드를 생각해보세요
-
24:39 - 24:42예제에선 데이터 객체를 하나만 만들어도 충분했지만
-
24:42 - 24:43예제에선 데이터 객체를 하나만 만들어도 충분했지만
-
24:43 - 24:48코드를 제대로 테스트하려면 종종
-
24:48 - 24:50거대하고 복잡한 테스트 데이터를 만들어야 하는 경우가 있습니다.
-
24:50 - 24:53거대하고 복잡한 테스트 데이터를 만들어야 하는 경우가 있습니다.
-
24:53 - 24:55그럼 테스트 데이터를 생성하는 코드를 정리해볼까요?
-
24:55 - 24:58여기 저기에 중복 코드들이 있지요
-
24:58 - 25:02빌더 패턴을 적용해보겠습니다.
-
25:02 - 25:04빌더 패턴을 적용해보겠습니다.
-
25:04 - 25:09기본적으로 이런 식으로 사용하게 되는 거지요
-
25:09 - 25:12테스트용 유저를 생성하는데
-
25:12 - 25:19
-
25:24 - 25:35ANOTHER_USER, loggedInUser와 친구이고
-
25:36 - 25:46브라질과 런던으로 여행을 가지요.
-
25:47 - 25:50이런 식으로 테스트 데이터를 만드는 거에요.
-
25:50 - 25:52가독성이 훨씬 좋습니다.
-
25:52 - 25:55그럼 실제로 구현을 해볼까요
-
25:55 - 25:58일단 build 메소드를 추가합시다.
-
25:58 - 26:00빌더 패턴을 적용하는 거지요.
-
26:02 - 26:05UserBuilder를 추가하고
-
26:07 - 26:12이런 모양을 원하는 겁니다.
-
26:12 - 26:16이 부분은 주석 처리하고요
-
26:17 - 26:25
-
26:25 - 26:28
-
26:31 - 26:34aUser 메소드를 만들고요
-
26:48 - 26:50메소드를 이어서 호출 할 수 있도록
-
26:50 - 26:53계속 같은 오브젝트를 반환합니다
-
26:53 - 26:55이러면 메소드를 계속 이어서 호출할 수 있게 됩니다.
-
26:55 - 26:57UserBuilder를 생성해서 반환하고
-
27:01 - 27:05다음 메소드를 만들어 봅시다
-
27:08 - 27:10역시 UserBuilder를 반환하고요
-
27:10 - 27:15두 개의 유저를 받고 있습니다.
-
27:17 - 27:22항상 말 했듯이 프로그래밍에는 딱 3개의 숫자만 존재합니다.
-
27:22 - 27:240, 1, 그리고 많이 입니다.
-
27:24 - 27:27그 밖에는 없어요.
-
27:27 - 27:30인자를 여러 개를 받도록 변경합시다
-
27:31 - 27:33필요한 수만큼 입력할 수 있도록
-
27:33 - 27:37가변길이 인자를 사용하겠습니다.
-
27:38 - 27:41이름은 friends 로 하고
-
27:43 - 27:48friends 변수에 할당하고
-
27:48 - 27:51계속 UserBuilder 인스턴스를 반환합니다.
-
27:51 - 27:53이 것도 만들어 주고요
-
27:53 - 27:58배열은 반드시 초기화 해야겠지요
-
27:58 - 28:01
-
28:05 - 28:08
-
28:09 - 28:16좋습니다. 다음 메소드를 만들어 볼까요
-
28:16 - 28:19
-
28:19 - 28:22똑 같이 하는 겁니다.
-
28:22 - 28:28메소드에 원하는 개 수만큼 여행 정보를 넣어 줄 수 있겠지요
-
28:37 - 28:39
-
28:39 - 28:41멤버 변수로 만들고요
-
28:41 - 28:45저는 보통
-
28:45 - 28:49읽기 좋은 모양으로 사용하는 코드를 작성하고
-
28:49 - 28:52그거에 맞춰서 구현 코드를 생성해갑니다.
-
28:53 - 28:57이 번엔 build 메소드를 생성합니다
-
28:57 - 29:04맨 밑으로 옮기는 게 좋겠지요
-
29:04 - 29:11build 메소드가 실제로 객체를 생성하게 됩니다.
-
29:12 - 29:15유저를 생성하고
-
29:16 - 29:17이 유저를 반환하게 하고
-
29:17 - 29:19그리고
-
29:20 - 29:23여행 목록을 추가하고
-
29:23 - 29:29친구들을 추가합니다.
-
29:32 - 29:38메소드 이름은 파라메터 이름과 함께 정하는 게 좋습니다.
-
29:38 - 29:41읽기에도 좋고
-
29:41 - 29:43이름에 중복을 피할 수도 있지요.
-
29:43 - 29:46여행 정보부터 시작할까요
-
29:46 - 29:50
-
29:50 - 29:52
-
29:52 - 29:56
-
29:56 - 30:03여행을 추가하고
-
30:04 - 30:07친구 목록도 똑같이 하겠습니다
-
30:07 - 30:09메소드를 만들고
-
30:11 - 30:12
-
30:12 - 30:15
-
30:15 - 30:17
-
30:17 - 30:20
-
30:20 - 30:22
-
30:22 - 30:28
-
30:29 - 30:31좋습니다.
-
30:31 - 30:36인피니테스트 결과도 녹색이군요.
-
30:36 - 30:40이제 이건 지우고요
-
30:41 - 30:44여기에도 UserBuilder를 사용할 수 있겠지요
-
30:44 - 30:45수정 합시다
-
31:15 - 31:19좋고요. 이 건 주석 처리하고.
-
31:19 - 31:22테스트를 다시 실행하면, 여전히 성공합니다.
-
31:22 - 31:24좋습니다.
-
31:25 - 31:30테스트 가독성이 조금 더 좋아졌습니다.
-
31:30 - 31:34이제 빌더 클래스를 별도 파일로 빼겠습니다.
-
31:34 - 31:35
-
31:35 - 31:37Type To New File 기능을 사용해서
-
31:39 - 31:41별도 클래스로 만들어서
-
31:41 - 31:48일단 TripServiceTest와 같은 위치에 두겠습니다.
-
31:48 - 31:49일단 TripServiceTest와 같은 위치에 두겠습니다.
-
31:50 - 31:52이제 분리되었지요.
-
31:52 - 31:56static import로 변경하고
-
31:56 - 31:59이 것도 static import로 변경하고
-
31:59 - 32:02코드 모양을 맞추겠습니다.
-
32:02 - 32:05좋고요.
-
32:05 - 32:09import를 정리하고요
-
32:09 - 32:12사용하지 않는 import가 있으면 안되겠지요
-
32:12 - 32:14좋습니다.
-
32:14 - 32:17이제 테스트가 훨씬 마음에 드네요
-
32:17 - 32:20테스트를 읽기가 훨씬 좋아졌네요.
-
32:20 - 32:23이제 커밋을 해야겠지요?
-
32:23 - 32:24봅시다
-
32:24 - 32:26
-
32:26 - 32:29
-
32:30 - 32:31
-
32:31 - 32:46UserBuilder created and used in TripServiceTest
-
32:46 - 32:49좋습니다.
-
32:53 - 32:56이제 진짜로 리팩토링을 시작할 수 있겠습니다.
-
32:56 - 32:58아까 이야기 했던 게요
-
32:58 - 33:00어디 있더라
-
33:02 - 33:07테스트는 얕은 부분부터 깊은 부분으로 작성하는데요
-
33:08 - 33:10하지만 리팩토링은 그 반대로 진행합니다.
-
33:10 - 33:13가장 깊은 부분에서 시작해서
-
33:14 - 33:17얕은 부분으로 가는 거지요.
-
33:18 - 33:19왜냐하면
-
33:19 - 33:22중간 부분부터 리팩토링을 하려면
-
33:22 - 33:24코드를 전부 이해하고 있어야 하기 때문입니다.
-
33:24 - 33:25일반적으로
-
33:27 - 33:30레거시 코드는 글로벌 변수가 많고
-
33:30 - 33:31의존관계도 복잡하고
-
33:31 - 33:34변수도 여기 저기서 세팅하고 사용되는데요.
-
33:34 - 33:37가장 깊은 부분부터 시작하면요
-
33:37 - 33:40가장 깊은 부분은 뭔가에 의존하지 않습니다.
-
33:40 - 33:43다른 곳에서 주는 정보를 사용해서
-
33:43 - 33:45뭔가 하는 게 일반적입니다.
-
33:45 - 33:48깊은 부분을 추출하면요
-
33:48 - 33:53깊은 부분부터 리팩토링을 시작하면
-
33:53 - 33:54코드가 점점 줄어들게 됩니다.
-
33:54 - 33:57소스 코드를 볼까요
-
33:57 - 34:00가장 깊은 부분은요
-
34:00 - 34:02아까 본 것처럼 여기입니다.
-
34:02 - 34:05이 코드가 실행되려면
-
34:05 - 34:07이게 true 여야 하는데
-
34:07 - 34:09여기에서 세팅되는군요.
-
34:09 - 34:12이 예제 코드는요
-
34:12 - 34:14가장 깊은 부분에서
-
34:14 - 34:15하는 일이 별로 없어요
-
34:15 - 34:18DAO를 호출하는 게 전부라서
-
34:18 - 34:21특별히 리팩토링 할게 없습니다.
-
34:21 - 34:25다음으로 깊은 부분은 여기인데요
-
34:26 - 34:28네 이 부분이요
-
34:28 - 34:30이 부분을 수정해보겠습니다.
-
34:32 - 34:34레거시 코드에 작업을 할 때에
-
34:34 - 34:36아주 중요한 것은
-
34:36 - 34:41메소드가 이렇게 큰 원인을 파악하는 겁니다.
-
34:41 - 34:44왜 이렇게 복잡할까요?
-
34:44 - 34:51일반적으로 메소드가 하는 일이 너무 많아서입니다.
-
34:51 - 34:53하는 일이 많으니 크기가 커지겠지요
-
34:53 - 34:58처리 알고리즘을 개선하거나
-
34:58 - 35:01메소드를 추출하는 것 보다 더 중요한 것은
-
35:01 - 35:03정말 이 일을
-
35:03 - 35:05이 메소드가 하는 게 적절한가를
-
35:05 - 35:08지속적으로 생각해봐야 합니다.
-
35:08 - 35:11이 경우에는
-
35:13 - 35:14이 부분을 보면
-
35:14 - 35:24유저 객체에 친구 정보를 요청하지요
-
35:24 - 35:28유저 객체에서 받은 친구 목록을
-
35:28 - 35:30하나 하나 검사해서
-
35:30 - 35:34로그인 유저가 포함돼있는지를 확인하고 있습니다.
-
35:34 - 35:36이 건 코드 스멜 중 Feature Envy 입니다.
-
35:36 - 35:40TripService 클래스가 User 클래스의 역할을 탐내는 거죠
-
35:40 - 35:42User 클래스가 되고 싶은 겁니다.
-
35:42 - 35:48여기서 Feature Envy를 풀려면 어떻게 해야지요?
-
35:48 - 35:53User 클래스가 친구 목록을 가지고 있다면
-
35:53 - 35:55User 클래스에게 물어보면 됩니다.
-
35:55 - 35:59"이봐 이 사람이랑 친구인가?" 라고요.
-
35:59 - 36:03그럼 이 부분을 옮기겠습니다.
-
36:03 - 36:05일단은요
-
36:05 - 36:07유저 클래스를 봅시다.
-
36:07 - 36:09어디 있지요?
-
36:10 - 36:13이 건 닫고
-
36:13 - 36:15여기 있네요.
-
36:17 - 36:19유저 클래스의 테스트를 작성합시다.
-
36:19 - 36:24이 건 유저 클래스고
-
36:24 - 36:30테스트 케이스를 새로 만들고요
-
36:30 - 36:35테스트 경로에 넣어야겠지요
-
36:36 - 36:40테스트 클래스가 만들어 졌고요
-
36:40 - 36:44이 쪽으로 옮기겠습니다.
-
36:44 - 36:47네 이제 뭘 해야지요?
-
36:47 - 36:53친구인지 확인하는 기능을 User 클래스로 옮길 겁니다.
-
36:53 - 36:55그냥 User 클래스에게 물어보는 거지요.
-
36:55 - 36:58"저 사람이랑 친구야?" 라고요.
-
36:58 - 36:59
-
36:59 - 37:02테스트 먼저 작성해야겠지요
-
37:06 - 37:11유저들이 친구가 아니면 친구가 아니라고 해야 한다
-
37:11 - 37:13친구가 아닌 경우부터 테스트하겠습니다.
-
37:13 - 37:15제일 쉬운 테스트이니까요
-
37:15 - 37:17가장 간단해요
-
37:19 - 37:22UserBuilder를 사용해야겠지요
-
37:22 - 37:35
-
37:35 - 37:45Bob과 친구라고 합시다
-
37:45 - 37:49
-
37:50 - 37:54그렇다면
-
37:55 - 38:13Paul과는 친구가 아니겠지요
-
38:15 - 38:21보시다시피 Bob과 친구인 유저를 하나 만들고
-
38:21 - 38:25유저에게 Paul과 친구냐고 물어보는 거지요
-
38:25 - 38:26당연히 친구가 아니겠지요
-
38:26 - 38:30Bob과 Paul을 생성하겠습니다
-
38:38 - 38:40Paul도 만들어야지요
-
38:49 - 38:53이 걸 User 클래스에 추가하려고요
-
38:53 - 38:55생성하겠습니다.
-
39:00 - 39:06이 테스트를 통과하기 위해 필요한 최소한의 구현은
-
39:06 - 39:08false를 반환하는 거지요.
-
39:08 - 39:10저장 하자마자
-
39:10 - 39:15인피니테스트가 테스트가 성공했다고 알려주네요.
-
39:15 - 39:16멋지군요.
-
39:17 - 39:23유저끼리 친구인 경우도 테스트하겠습니다.
-
39:23 - 39:35유저들이 친구인 경우 친구라고 알려야 한다.
-
39:38 - 39:42이 코드를 가져오겠습니다
-
39:42 - 39:44밑으로 내려서
-
39:44 - 39:47어떻게 할거냐 하면
-
39:47 - 39:51Paul과도 친구로 해주고
-
39:51 - 39:58그러면 당연히
-
39:58 - 40:00true가 되겠지요
-
40:00 - 40:05테스트를 통과하지 못했군요
-
40:06 - 40:08실제로 구현을 해야겠지요
-
40:08 - 40:11
-
40:13 - 40:14파라메터 이름으로 Paul은 맞지 않겠지요
-
40:14 - 40:18anotherUser로 변경하고요
-
40:26 - 40:32좋습니다. 메소드 구현이 되었네요.
-
40:32 - 40:36테스트로 실행도 되고 있지요.
-
40:36 - 40:38이 부분은 실행되지 않았지만
-
40:38 - 40:40상관없지요.
-
40:41 - 40:44다시 커밋하기에 좋은 타이밍이지요?
-
40:46 - 40:48
-
40:48 - 40:49
-
40:49 - 40:51
-
40:55 - 41:03isFriendsWith(user) method added to User
-
41:04 - 41:06좋습니다.
-
41:08 - 41:12다시 테스트로 돌아가서
-
41:12 - 41:19커버리지 결과는 지우고요
-
41:21 - 41:24이제 User 클래스에 새로운 행위가 추가되었습니다.
-
41:25 - 41:26리팩토링을 할 때에는요
-
41:26 - 41:29항상 테스트가 성공하는 상태로 작업할 수 있게 노력해야 합니다.
-
41:29 - 41:31항상 테스트가 성공하는 상태로 작업할 수 있게 노력해야 합니다.
-
41:31 - 41:33계획 없이 마구 고쳐버리면 안됩니다.
-
41:34 - 41:36이것 저것 마구 고쳐서
-
41:36 - 41:37테스트들이 마구 깨져버리면 안됩니다.
-
41:37 - 41:40하나씩 수정해 가면서
-
41:40 - 41:42테스트를 같이 실행해야 합니다.
-
41:42 - 41:44인피니테스트를 사용하는 것도 좋은 방법입니다.
-
41:44 - 41:47인피니테스트를 사용하는 것도 좋은 방법입니다.
-
41:47 - 41:50코드를 저장할 때마다 테스트를 실행해서
-
41:50 - 41:51바로 바로 결과를 알 수 있습니다.
-
41:51 - 41:55뭔가 수정을 했는데
-
41:55 - 41:57실수를 했다면
-
41:57 - 42:00테스트 결과를 보고 바로 알아 챌 수 있습니다.
-
42:00 - 42:04그러면 Ctrl+Z 한 번으로 복구할 수 있습니다.
-
42:04 - 42:06Ctrl+Z를 누르고 저장만하면 됩니다.
-
42:06 - 42:07바로 문제없던 상태로 돌아갈 수 있습니다.
-
42:07 - 42:12이제 안전하게 리팩토링을 할 수 있게
-
42:12 - 42:16가능한 작게 변경을 해보겠습니다.
-
42:19 - 42:22자
-
42:22 - 42:29여기 불린 변수를 옮겨볼까요?
-
42:29 - 42:34이 if 구문 안에서만 사용되고 있으니까요
-
42:34 - 42:35또 다른 팁을 알려드리면요
-
42:35 - 42:36레거시 코드로 작업을 하다 보면
-
42:36 - 42:40변수들이 여기 저기에 선언된 경우가 있는데요
-
42:40 - 42:42가능한 인접하게 모으세요
-
42:42 - 42:48사용되는 위치에 가깝게 모으세요.
-
42:48 - 42:52변수가 코드의 위 부분에서는 사용되지 않고
-
42:52 - 42:53밑에서만 사용되고 있는데
-
42:53 - 42:54맨 위에 선언되어있다면
-
42:54 - 42:56사용되는 근처로 변수 선언을 옮기세요
-
42:56 - 42:58그러면 연관된 코드들이 모여있게 되므로
-
42:58 - 43:01코드를 분리하기에도 좋습니다.
-
43:01 - 43:03이제
-
43:03 - 43:10친구인지를 바로
-
43:10 - 43:17알 수 있지요
-
43:17 - 43:22loggedUser보다 loggedInUser가 낫군요
-
43:22 - 43:24이렇게요
-
43:24 - 43:25기존 코드에 작업을 하다가
-
43:25 - 43:29이름이 이상하거나 적당하지 않으면
-
43:29 - 43:30그 순간에 바로
-
43:30 - 43:32이름을 바꾸세요.
-
43:32 - 43:33좋습니다. 여기 보이죠
-
43:33 - 43:38여기 초록색 상태바를 계속 보는 거에요
-
43:38 - 43:39코드를 저장할 때마다
-
43:39 - 43:41수정된 부분의 테스트가 실행돼서 결과가 표시됩니다.
-
43:41 - 43:47이제 이 부분을 주석 처리할 수 있겠지요
-
43:47 - 43:49저장을 하고요
-
43:49 - 43:53테스트 결과를 보니 정상이네요.
-
43:53 - 43:54이제 지우면 되겠네요.
-
43:55 - 43:57이 것도 지우고요
-
43:58 - 44:00그러면 아마도..
-
44:10 - 44:13이 건 인라인하고요
-
44:13 - 44:14좋습니다.
-
44:15 - 44:16깔끔하네요.
-
44:18 - 44:23이 것도 금방 바꿀 수 있겠지요
-
44:23 - 44:26이 부분이요
-
44:26 - 44:28Guard Clause 패턴을 적용할 수 있겠지요?
-
44:28 - 44:30이 부분을 Guard Clause로 바꿀 수 있습니다.
-
44:30 - 44:31Guard Clause는요
-
44:31 - 44:33파라메터가 들어오자마자
-
44:33 - 44:36유효성을 검증하는 것이지요.
-
44:36 - 44:37
-
44:37 - 44:40그러면
-
44:40 - 44:45이 부분을 Guard Clause로 변경해서
-
44:45 - 44:46이 if 문을 없애보겠습니다.
-
44:48 - 44:53loggedInUser가 Null이면
-
44:56 - 44:58
-
45:00 - 45:03예외를 던지는 거지요.
-
45:03 - 45:07이 게 위에 있어야겠지요.
-
45:09 - 45:12저장을 하면 바로
-
45:12 - 45:14녹색으로 표시되네요.
-
45:14 - 45:20친절하게도 이클립스가 이 부분은 실행되지 않는 코드라고 알려주네요.
-
45:20 - 45:24이제 if 문을 삭제할 수 있겠습니다.
-
45:28 - 45:29저장하고
-
45:30 - 45:31좋습니다.
-
45:31 - 45:35지금 리팩토링하는 동안에 한 번도 테스트가 실패하지 않고 있지요.
-
45:35 - 45:37지금 리팩토링하는 동안에 한 번도 테스트가 실패하지 않고 있지요.
-
45:37 - 45:40혹시 실수를 해서 실패하는 테스트가 생겨도요
-
45:40 - 45:43실제 업무 중에는 그럴 수도 있겠지요
-
45:43 - 45:46Ctrl+Z를 누르면 되요.
-
45:46 - 45:47금방 이전 상태로 되돌릴 수 있습니다.
-
45:47 - 45:48그리고 뭐가 잘못된건지 생각하면 됩니다.
-
45:48 - 45:51테스트를 유지할 수 없는 경우도 있겠지만
-
45:51 - 45:54가능한 피해야 합니다.
-
45:54 - 45:57이제 코드가 두 개의 블럭으로 구분되는군요.
-
45:59 - 46:06첫 번째 블럭은 유저 정보를 검증하고요
-
46:06 - 46:14두 번째 블럭은 친구의 여행 정보를 조회합니다.
-
46:14 - 46:17여기서도 조금 더 개선이 가능하겠네요
-
46:17 - 46:20어디 봅시다
-
46:23 - 46:26레거시 코드에서 변수를 제거하는 것도
-
46:26 - 46:29시도해 볼 만한 것 입니다.
-
46:29 - 46:32변수 개 수가 적을 수록 코드는 더 쉬워지거든요.
-
46:32 - 46:37조금은 중복이 되거나
-
46:37 - 46:39메소드를 두 번 호출하게되서
-
46:39 - 46:41약간의 성능이 희생되는 경우에도 가치가 있습니다.
-
46:42 - 46:44변수를 줄이는 것은요
-
46:44 - 46:47변수는 여러 곳에서 값이 세팅되고 참조되기 때문에
-
46:47 - 46:50긴 코드를 파악하기 어렵게 합니다.
-
46:50 - 46:52변수를 없애고
-
46:52 - 46:56메소드를 반복해서 호출하면
-
46:56 - 46:58코드를 이해하기 쉬워집니다.
-
46:58 - 47:02그런 후에 꼭 필요한 경우에만 최적화 하면 됩니다.
-
47:02 - 47:10예를 들어 이런 식으로요.
-
47:16 - 47:18저장을 하면
-
47:18 - 47:19문제 없지요?
-
47:20 - 47:26이런 식으로도 변경할 수 있을 거에요
-
47:26 - 47:27
-
47:30 - 47:32문제 없고요
-
47:33 - 47:35
-
47:36 - 47:37이 건 필요 없지요.
-
47:37 - 47:40그대로입니다.
-
47:40 - 47:42이 변수는 이제 필요 없지요
-
47:42 - 47:44이 것도 필요 없고요
-
47:44 - 47:46테스트는 정상이에요
-
47:46 - 47:49삼항 연산자를 써서
-
47:49 - 47:53조금 더 간단하게 해볼까요
-
47:53 - 47:58예를 들어서
-
48:12 - 48:14저장을 하면
-
48:14 - 48:15여전히 녹색이네요.
-
48:15 - 48:16좋습니다.
-
48:16 - 48:18그 다음에
-
48:18 - 48:21이 변수는 두 군데에 사용되고 있는데요
-
48:21 - 48:22인라인 시키겠습니다.
-
48:22 - 48:24
-
48:24 - 48:26둘 다 인라인 되었지요.
-
48:26 - 48:27이런 식이지요.
-
48:27 - 48:29변수를 없앴습니다.
-
48:29 - 48:33물론 메소드를 두 번 호출하고 있어요
-
48:33 - 48:35하지만 코드는 더 간단해졌지요.
-
48:35 - 48:36물론 이렇게 해서 성능에 문제가 생기면
-
48:37 - 48:40메소드 호출을 줄이도록 수정해야겠지요.
-
48:40 - 48:45좋습니다. 훨씬 좋아졌네요.
-
48:45 - 48:49이 부분은 마음에 들지않는군요..
-
48:51 - 48:55메소드로 만들겠습니다.
-
48:55 - 48:58
-
49:02 - 49:04이렇게 하면 좋은 점은요
-
49:06 - 49:08예를 들어서요
-
49:10 - 49:12코드를 보면
-
49:12 - 49:14테스트를 보면요
-
49:15 - 49:19테스트 이름은 유저가 로그인하지 않은 경우에 예외를 던진다
-
49:19 - 49:21로그인 된 유저를 조회한다
-
49:21 - 49:22예외를 던진다
-
49:22 - 49:27테스트는 유저가 친구가 아니면 여행 정보를 반환하지 않는다
-
49:27 - 49:30유저가 로그인 유저와 친구라면
-
49:30 - 49:31여행 정보가 없고
-
49:31 - 49:34테스트는 유저가 친구들인 경우에 여행 정보를 조회한다
-
49:34 - 49:36로그인 유저와 친구인 경우
-
49:36 - 49:38유저의 여행 목록
-
49:38 - 49:45이런 식으로 테스트에 사용하는 용어들이
-
49:45 - 49:46제품 코드 구현에도 그대로 사용되게 됩니다.
-
49:46 - 49:48제품 코드 구현에도 그대로 사용되게 됩니다.
-
49:48 - 49:50꼭 이런 식으로 작성해보시길 바랍니다.
-
49:50 - 49:55잠시 멈추고 커밋하겠습니다.
-
49:55 - 49:57
-
49:57 - 49:59
-
49:59 - 50:02
-
50:03 - 50:08TripService refactored
-
50:09 - 50:10좋습니다.
-
50:12 - 50:14훌륭해요.
-
50:15 - 50:20이 정도면 충분하겠지요?
-
50:20 - 50:22마무리할까요?
-
50:22 - 50:23아닙니다.
-
50:23 - 50:26아직 한참 멀었습니다.
-
50:28 - 50:30여기 이 짧은 코드요
-
50:31 - 50:35어마 어마한 문제점을 가지고 있습니다.
-
50:36 - 50:39그 중 하나를 이야기하면요
-
50:39 - 50:41우리는 지금까지
-
50:41 - 50:46테스트가 없는 레거시 코드에
-
50:46 - 50:48테스트를 작성했습니다.
-
50:48 - 50:50그렇게 해서
-
50:50 - 50:52문제 없이 코드를 수정할 수 있다는
-
50:52 - 50:56자신감이 생겼어요. 대단한 일이지요.
-
50:56 - 50:59
-
50:59 - 51:02하지만 반대로 단점도 있는데요
-
51:02 - 51:04기존 코드의 디자인이 나쁜 경우에는 어떻게 될까요?
-
51:06 - 51:08기존 코드의 디자인이 나쁜데
-
51:08 - 51:10그 위에 테스트를 작성해 놓으면
-
51:10 - 51:13디자인을 수정할 수 없게 만들어 버립니다.
-
51:13 - 51:15매우 주의해야 해요
-
51:15 - 51:19원래 디자인 변경은 쉽게 하는 일이 아닌데요
-
51:19 - 51:22그 위에 테스트들을 얹어버리면
-
51:22 - 51:24더 변경하기 힘들게 됩니다.
-
51:24 - 51:25디자인을 변경하는 순간
-
51:25 - 51:27작성했던 테스트들도 같이 수정해야 하므로
-
51:27 - 51:29주의해야 합니다.
-
51:29 - 51:32그럼 이 디자인은 뭐가 문제일까요?
-
51:32 - 51:35아마도
-
51:36 - 51:41TripService는 서버단 코드이고
-
51:41 - 51:43(MVC 중) 모델에 속할 겁니다.
-
51:44 - 51:51그런데 User Session을 직접 참조하고 있네요
-
51:51 - 51:57모델이 웹 프레임워크를 참조하면 안되지요
-
51:57 - 52:01Http Session 같은 것들을 알고 있으면 안됩니다.
-
52:02 - 52:05이 클래스는 User Session을 직접 참조하는 게 잘못된 겁니다.
-
52:05 - 52:10해결을 해야 할텐데요
-
52:10 - 52:12선택할 수 있는 하나의 방법은
-
52:12 - 52:17로그인 유저를 메소드에 인자로 전달하는 겁니다.
-
52:17 - 52:23이제 위험한 리팩토링을 시작하겠습니다.
-
52:23 - 52:26오늘 연습이야 별 문제 없겠습니다만
-
52:26 - 52:29실제 업무에서 할 때에는
-
52:29 - 52:30주의를 기울이셔야 합니다.
-
52:30 - 52:31조심스럽게
-
52:31 - 52:33언제나 처럼
-
52:33 - 52:35조금씩 작업하겠습니다.
-
52:35 - 52:40메소드 파라메터로 로그인 유저를 전달하게 할 건데요
-
52:40 - 52:43사용할 수 있는 방법은
-
52:43 - 52:46리팩토링에서
-
52:46 - 52:48메소드 시그니처를 수정해서
-
52:48 - 52:51파라메터를 추가하겠습니다.
-
52:51 - 52:53
-
52:55 - 52:59
-
52:59 - 53:01기본 값은 Null이고요
-
53:02 - 53:04좋습니다.
-
53:04 - 53:08이클립스의 리팩토링 기능은 매우 강력합니다.
-
53:08 - 53:10인텔리제이도 그렇고요.
-
53:10 - 53:13이 유저 파라메터는
-
53:13 - 53:15사용되고 있지는 않지요.
-
53:15 - 53:18테스트 코드를 보면
-
53:18 - 53:23Null을 전달하는 것으로 변경되어 있네요.
-
53:23 - 53:26멋지군요.
-
53:26 - 53:29여기서 문제는요
-
53:29 - 53:32이 메소드는 제품 코드이니
-
53:32 - 53:34이미 사용되고 있을 거에요
-
53:34 - 53:37아마 여러 군데서 사용 중이겠지요.
-
53:37 - 53:45따라서 사용되는 모든 곳에서
-
53:45 - 53:47Null을 넣어주고 있을 테니
-
53:47 - 53:50모든 곳에서 전부 수정을 해줘야 합니다.
-
53:50 - 53:52잊지 마시기 바랍니다.
-
53:53 - 53:56일단 지금
-
53:58 - 54:00해야 할 것은
-
54:00 - 54:01테스트를 수정해야지요
-
54:01 - 54:04여기 Null을 전달하는 게 보기 싫군요.
-
54:04 - 54:05로그인 유저를 전달해야 하니
-
54:05 - 54:08게스트를 넣어주고요
-
54:08 - 54:10
-
54:12 - 54:15인피니테스트 결과를 확인하고요
-
54:15 - 54:22여기에는 로그인 된 유저를 넣어야지요
-
54:22 - 54:24여기에는
-
54:27 - 54:30네 여기에도 로그인 된 유저를 넣겠습니다.
-
54:36 - 54:37좋습니다.
-
54:37 - 54:39한 번 더 확인해보고요.
-
54:39 - 54:41좋습니다.
-
54:42 - 54:45이제 전달되는 파라메터를 사용해야겠지요
-
54:47 - 54:48이제
-
54:49 - 54:54전달된 걸 사용할거에요.
-
54:54 - 54:59파라메터를 사용하게 변경할겁니다.
-
54:59 - 55:04여기 메소드 호출을
-
55:04 - 55:07파라메터로 변경하고요
-
55:07 - 55:09저장을 하면..
-
55:11 - 55:14테스트 결과는 정상입니다.
-
55:14 - 55:16그러면 이 메소드는요
-
55:16 - 55:19private으로 변경하면
-
55:19 - 55:22사용 안 된다고 표시가 되지요.
-
55:22 - 55:25삭제해도 안전하겠군요.
-
55:26 - 55:30삭제한 후에
-
55:30 - 55:32이 것도 삭제할 수 있겠지요.
-
55:32 - 55:36다시 정상으로 돌아왔지요.
-
55:36 - 55:37유저를 파라메터로 전달하게
-
55:37 - 55:40리팩토링 했습니다.
-
55:41 - 55:43이제 할 것은
-
55:43 - 55:46이 loggedInUser 필드는
-
55:46 - 55:49필요할 것 같지 않네요.
-
55:49 - 55:55이렇게 바꾸면요
-
55:56 - 55:59여기도요
-
56:02 - 56:06그러면 이클립스가
-
56:06 - 56:10이 변수가 사용되지 않는다고 알려주지요.
-
56:10 - 56:14삭제할 수 있겠군요.
-
56:14 - 56:17삭제하고요.
-
56:17 - 56:19이 것도요
-
56:19 - 56:23테스트도 정상입니다.
-
56:23 - 56:26커밋을 해야겠네요.
-
56:26 - 56:29오. 여기 경고가 있네요
-
56:29 - 56:33사용하지 않는 import는 지워야지요
-
56:33 - 56:35깔끔하게요.
-
56:35 - 56:37장인정신은 중요합니다.
-
56:37 - 56:39항상 깔끔하게 유지해야지요.
-
56:40 - 56:42좋습니다. 잠시 멈추고요.
-
56:42 - 56:44
-
56:45 - 56:47
-
56:50 - 57:01Logged in user passed in to TripService
-
57:02 - 57:04좋습니다.
-
57:06 - 57:09대충 마무리됐을까요.
-
57:11 - 57:13
-
57:13 - 57:17이 부분은 여전히 마음에 안 드네요.
-
57:18 - 57:19TripDAO의
-
57:19 - 57:21static 메소드를 직접 호출하고 있지요.
-
57:21 - 57:25그래서 이런 식으로 해야만 했어요.
-
57:25 - 57:30말했듯이 임시적인 방법인데요
-
57:30 - 57:34제대로 한다면 TripDAO를 주입해서
-
57:34 - 57:36멤버 변수를 사용하게 만들어야 하는데요
-
57:36 - 57:39static 메소드라서
-
57:39 - 57:41다른 것으로 대체하기가 어렵지요.
-
57:41 - 57:43왜냐하면 일반적으로
-
57:43 - 57:46인스턴스만 주입이 가능하고 클래스는 불가하기 때문에
-
57:46 - 57:48Mock으로 대체할 수 없습니다.
-
57:48 - 57:51그럼 조금 더 수정해서
-
57:51 - 57:53이 부분을 없애고
-
57:53 - 57:56TripDAO의 멤버 변수를 만들어 사용하게 해보겠습니다.
-
57:56 - 57:58TripDAO 클래스를 열어
-
57:58 - 57:59구현 내용을 보면요
-
57:59 - 58:00지금 이 코드는
-
58:00 - 58:02단순한 예제 코드라 예외를 던지고 끝나지만요
-
58:02 - 58:05단순한 예제 코드라 예외를 던지고 끝나지만요
-
58:06 - 58:10앞에 이야기한 것처럼
-
58:10 - 58:12보통은 데이터베이스나
-
58:12 - 58:13분산 캐시 같은
-
58:13 - 58:18저장하는 무언가에 엑세스하겠지요.
-
58:19 - 58:21자 이제
-
58:22 - 58:29멤버 메소드를 만들건대요.
-
58:30 - 58:35먼저 테스트를 작성해야겠지요.
-
58:38 - 58:39
-
58:43 - 58:46
-
58:47 - 58:49테스트 폴더에 넣고
-
58:52 - 58:54이 것도 흥미로운 기법이니
-
58:56 - 58:57눈 여겨 보세요.
-
58:58 - 59:03기존 Static 메소드와 동일한 기능을 하는
-
59:03 - 59:09인스턴스 메소드를 만들 거에요.
-
59:12 - 59:13똑 같이 동작하게요.
-
59:16 - 59:18그냥 Static 키워드를 없애도 가능하겠지요.
-
59:18 - 59:23여기 static 글자만 지워서요
-
59:23 - 59:24물론 가능합니다만
-
59:24 - 59:25그렇게 하면
-
59:25 - 59:28여기 저기 수정할 게 많을 겁니다.
-
59:28 - 59:32어플리케이션에서 이 메소드를 사용하는 클래스가
-
59:32 - 59:35여러 개 있는 상황에서
-
59:35 - 59:38이걸 인스턴스 메소드로 바꿔버리면
-
59:38 - 59:40전부 컴파일 에러가 나겠지요.
-
59:40 - 59:43그러면 일이 너무 커져버립니다.
-
59:43 - 59:45우린 조금씩 안전하게 작업을 할거니까요
-
59:46 - 59:48그래서
-
59:49 - 59:51테스트를 만들겠습니다.
-
59:51 - 59:53예제 DAO를 테스트하는 거니까
-
59:53 - 59:55이 테스트가 검증하는 디테일 한 부분에는
-
59:55 - 59:57집중하실 필요는 없습니다.
-
59:57 - 60:01그냥 예제 DAO의 동작을 검증하겠습니다.
-
60:01 - 60:20유저의 여행정보를 조회하면 예외를 발생하는 거지요
-
60:21 - 60:26예외가 발생해야겠지요.
-
60:28 - 60:30말씀드린대로
-
60:30 - 60:34원래는 데이터를 조회해야겠지만
-
60:34 - 60:36이 예제 코드에서는
-
60:36 - 60:40바로 예외를 던지지요.
-
60:40 - 60:42이거지요.
-
60:42 - 60:45인스턴스 메소드를 사용하려고 하니까요
-
60:45 - 60:48메소드를 호출하기만 하면 되겠지요
-
60:48 - 60:49그러니까
-
60:49 - 60:52
-
60:55 - 61:06
-
61:10 - 61:13인스턴스가 필요하지요
-
61:15 - 61:16
-
61:16 - 61:18메소드를 생성해서
-
61:19 - 61:26기존 거랑 똑 같이 동작하게 할 겁니다
-
61:33 - 61:35테스트는 실패하고 있지요. 당연합니다.
-
61:35 - 61:37이제는요
-
61:37 - 61:40기존 static 메소드랑 똑 같이 동작해야 하니까요
-
61:40 - 61:43
-
61:45 - 61:52이 안에서
-
61:52 - 61:53기존 메소드를 호출합니다.
-
61:54 - 61:56이렇게 하는 이유는요
-
61:56 - 61:57이렇게 하는 이유는요
-
61:57 - 62:03점진적으로 기존 코드들을 새 메소드를 사용하게 바꿔가려는 거지요.
-
62:05 - 62:06여기에서
-
62:06 - 62:09TripService가 인스턴스 메소드를 호출하게 하는 거지요
-
62:09 - 62:11이렇게 시작해서
-
62:11 - 62:13Static 메소드를 사용하는 클래스가 없어질 때까지
-
62:13 - 62:19계속 바꿔나가는 겁니다.
-
62:19 - 62:20그런 후에
-
62:20 - 62:22이 Static 메소드의 내용을 복사해서
-
62:22 - 62:24여기로 옮기고 Static 메소드는 지워버리면
-
62:24 - 62:26끝이지요.
-
62:26 - 62:27유용한 기법이에요.
-
62:27 - 62:29다시 돌아가서
-
62:29 - 62:35인스턴스 메소드가 완성됐습니다.
-
62:35 - 62:38이제는
-
62:38 - 62:44TripDAO를 인젝트 해야 하는데요
-
62:44 - 62:46
-
62:46 - 62:49방법은 여러 가지가 있습니다. 예를 들어
-
62:49 - 62:51DI 프레임워크 없이 그냥 할 수도 있겠지요
-
62:51 - 62:55그냥 TripService의 생성자에 TripDAO를 넣어주면 됩니다.
-
62:55 - 62:57그렇게 하는 게
-
62:57 - 62:59가장 간단한 방법입니다.
-
62:59 - 63:03그래도 대부분의 자바 개발자들은
-
63:03 - 63:06Spring, Mockito 등을 주로 사용하니까요
-
63:06 - 63:08Mockito를 이용하는 방법을 보여드리겠습니다.
-
63:08 - 63:11만약에 Mockito를 사용하지 않으면
-
63:11 - 63:12그냥 심플하게
-
63:12 - 63:17생성자를 만들어 TripDAO를 건네주면 됩니다.
-
63:17 - 63:19그럼 Spring, Mockito를 사용해서
-
63:19 - 63:22테스트하는 방법을 보여드리겠습니다.
-
63:22 - 63:27이제 TripDAO를 인젝트 할 수 있도록
-
63:27 - 63:31테스트를 변경하도록 하겠습니다.
-
63:31 - 63:33이제
-
63:33 - 63:36JUnit과 Mockito를 사용할 거니까
-
63:36 - 63:37
-
63:37 - 63:39
-
63:42 - 63:47Mockito나 JUnit을 사용하지 않는 사람에게는
-
63:47 - 63:48좀 복잡해 보일 겁니다.
-
63:48 - 63:52그렇다면 관련 문서들을 읽어보시기 바랍니다.
-
63:55 - 64:01지금까지는 TestableTripService을 사용하고 있었는데요
-
64:01 - 64:03지금까지는 TestableTripService을 사용하고 있었는데요
-
64:04 - 64:07이제 TripService를 바로 사용하겠습니다.
-
64:07 - 64:09직접 생성하지 않고 주입 받아서요.
-
64:09 - 64:13TestableTripService를 이제 지워야겠어요
-
64:13 - 64:15더 이상 필요가 없습니다.
-
64:16 - 64:19의존하는 것들은 Mock으로 바꿔서 테스트할 수 있게
-
64:19 - 64:21조금씩 안전하게 작업하겠습니다.
-
64:21 - 64:24TripService를 하나 더 만들고요
-
64:24 - 64:26TripService를 하나 더 만들고요
-
64:27 - 64:37일단 realTripService라고 하고
-
64:37 - 64:38이건 진짜 TripService의 인스턴스에요
-
64:38 - 64:41Mock을 만들고요
-
64:52 - 64:55TripDAO의 Mock이요
-
65:00 - 65:03Mockito 기능을 사용해서
-
65:04 - 65:06TripService가
-
65:06 - 65:10Mock을 사용하게 하겠습니다.
-
65:11 - 65:21Mockito가 어떻게 동작하는지는 오늘 주제에서 조금 벗어나지만
-
65:21 - 65:26간단하게 설명 드리면
-
65:26 - 65:28실제 TripService 인스턴스가 생성될 때에
-
65:28 - 65:30Mockito가 TripService 클래스를 분석해서
-
65:31 - 65:35TripService가 사용하는 Mock들을 자동으로 주입시키는 겁니다.
-
65:35 - 65:37Mock DAO는 여기에 있고요
-
65:37 - 65:41이제 TripService가 이 Mock TripDAO가 필요하다는 것만 명시하면 됩니다.
-
65:42 - 65:44일단 그 전에
-
65:44 - 65:47이제 이 TripService의 인스턴스를요
-
65:47 - 65:49
-
65:49 - 65:49
-
65:49 - 65:51조금씩 조금씩
-
65:51 - 65:55realTripService로 바꾸고 저장..
-
65:56 - 66:00테스트 결과는 정상이지요
-
66:01 - 66:03지금 하려는 거는요
-
66:03 - 66:06테스트에서 사용 중인 TestableTripService를
-
66:06 - 66:07조금씩
-
66:08 - 66:09TripService 인스턴스로 바꾸는 겁니다.
-
66:10 - 66:11
-
66:11 - 66:13이렇게 하면
-
66:14 - 66:16정상이지요.
-
66:16 - 66:17지금 테스트가 통과하는 이유는
-
66:17 - 66:20TripDAO를 사용하지 않기 때문이에요.
-
66:20 - 66:22이 코드를 사용하지 않는 테스트들인 거지요.
-
66:24 - 66:31이 테스트도 바꾸면요..
-
66:32 - 66:35실패하는 군요.
-
66:35 - 66:37이거지요.
-
66:37 - 66:39TripService 인스턴스에서
-
66:39 - 66:44TripDAO의 Static 메소드를 호출했기 때문에
-
66:44 - 66:48TripDAO의 Static 메소드를 호출했기 때문에
-
66:48 - 66:49예외가 떨어진 거에요.
-
66:49 - 66:51이제 실패한 테스트가 있으니
-
66:51 - 66:54제품 코드를 수정해야겠네요.
-
66:57 - 66:59그래서
-
67:00 - 67:02제품 코드에서요
-
67:02 - 67:04스프링인 경우지요
-
67:04 - 67:06
-
67:18 - 67:22import 하고
-
67:22 - 67:25TripDAO는 XML에 스프링 빈으로 등록했다고 하고요
-
67:25 - 67:27TripDAO는 XML에 스프링 빈으로 등록했다고 하고요
-
67:27 - 67:32
-
67:32 - 67:33좋습니다.
-
67:35 - 67:37화면을 늘리고
-
67:38 - 67:40
-
67:40 - 67:42이러면 Mockito가
-
67:42 - 67:44이 Mock TripDAO를
-
67:44 - 67:47여기 TripService에 넣어줄 겁니다.
-
67:49 - 67:52그래서 이 TripDAO를 사용하게 해야 하는데요
-
67:52 - 67:55테스트는 실패하고 있지요?
-
67:55 - 67:57이 TripDAO를 사용하려면
-
67:57 - 68:02이거 대신에
-
68:05 - 68:07일단 테스트를 돌리면요
-
68:07 - 68:10차이점을 보려고요
-
68:29 - 68:31실행하고
-
68:33 - 68:35계속 단축키를 잘 못 누르네요
-
68:35 - 68:40아직 실제 DAO를 호출해서 에러가 나고 있는 겁니다.
-
68:40 - 68:45그래서 인스턴스 메소드를 호출하게 하고
-
68:45 - 68:49기존 Static 메소드 대신에
-
68:49 - 68:51인스턴스 메소드를 사용하게 해서
-
68:52 - 68:56테스트를 실행하면
-
68:56 - 69:00에러가 변했지요
-
69:00 - 69:03이제 Mock DAO를 사용하고 있는 겁니다.
-
69:03 - 69:07화면은 줄이고
-
69:07 - 69:12이젠 Mock DAO가 실행된 겁니다.
-
69:13 - 69:18MockDAO가 여행 정보 2개를 반환되었어야 하는데 아무것도 없었던 거지요.
-
69:18 - 69:20원래 테스트 시나리오는
-
69:20 - 69:22여행 정보 2개가 필요하니까요
-
69:24 - 69:26이제..
-
69:26 - 69:28이렇게 되는 이유는
-
69:28 - 69:31TipService가 Mock TripDAO를 호출하는데
-
69:31 - 69:33Mock TripDAO가 어떻게 동작해야 할지 설정하지 않아서 에요.
-
69:33 - 69:38그러니까
-
69:41 - 69:44
-
69:44 - 69:49
-
69:52 - 69:58이 메소드가 호출이 되면
-
69:59 - 70:05친구의 여행 목록을 반환해야지요
-
70:05 - 70:08지금 하는 건
-
70:08 - 70:16Mock DAO가 여행 목록을 반환하게 하는 거지요
-
70:16 - 70:19이 코드가 했던 것과 동일하게
-
70:19 - 70:22Mock이 동작하도록 하는 겁니다.
-
70:22 - 70:25이제 테스트가 통과하는군요
-
70:27 - 70:31이제 이걸 private로 바꿀 수 있겠지요
-
70:33 - 70:37이 메소드는 이제 필요가 없고요
-
70:39 - 70:41전체 테스트를 다시 돌리면
-
70:41 - 70:43모두 정상이군요.
-
70:43 - 70:46이 클래스가 더 이상 필요 없다는 말이지요
-
70:48 - 70:51그건 이 것도 필요 없다는 거고
-
70:51 - 70:54이 게 필요 없으면 이 것도 필요 없지요
-
70:54 - 70:57이제 이 필드는 사용되지 않네요
-
70:57 - 70:59삭제하고요
-
71:00 - 71:02그러면 이제
-
71:02 - 71:04realTripService는 이름을 바꿔야겠지요
-
71:04 - 71:06이제 real은 의미가 없으니까요
-
71:06 - 71:07테스트를 작성할 때에
-
71:07 - 71:14testee, mock 같은 이름을 사용하는 경우가 종종 있는데요
-
71:14 - 71:15실제로 무엇인지를 표현하면 됩니다.
-
71:16 - 71:20이건 TripService이니까 그냥 tripService로 충분합니다. 그렇지요?
-
71:20 - 71:23좋습니다.
-
71:24 - 71:27이제 Test Double 클래스들도 필요 없고
-
71:27 - 71:30테스트도 읽기 편합니다.
-
71:31 - 71:35테스트를 한 번 더 돌려서 확인하고요.
-
71:35 - 71:38여기는 이렇게 바꿀 수 있겠지요.
-
71:44 - 71:46
-
71:47 - 71:49앞에 이야기한 것처럼
-
71:49 - 71:54테스트 메소드들을 접으면
-
71:55 - 72:00이렇게 되는 거지요.
-
72:00 - 72:03유저가 로그인하지 않은 경우 예외를 던진다
-
72:03 - 72:05여기에서 로그인 정보를 확인하지요
-
72:06 - 72:09유저들이 친구가 아닌 경우 여행 목록을 반환하지 않는다
-
72:09 - 72:13친구가 아닌 경우에는 빈 목록을 반환하고요
-
72:14 - 72:18유저가 서로 친구인 경우에는
-
72:18 - 72:19여행 정보를 반환하지요
-
72:19 - 72:22로그인 유저와 친구인 경우에
-
72:22 - 72:24여행 목록을 반환합니다.
-
72:28 - 72:31이 getter 메소드는 좀 별로네요
-
72:31 - 72:36getTripsByUser는요
-
72:36 - 72:39getFriendTrips로
-
72:40 - 72:42훨씬 낫지요?
-
72:42 - 72:46이 변수도
-
72:49 - 72:51friend가 좋겠네요
-
72:52 - 72:54이런 식이지요
-
72:56 - 72:59테스트가 잘 작성되어있으므로
-
72:59 - 73:02이런 것들을 해볼 수가 있는 겁니다.
-
73:03 - 73:07잠시 멈추고 커밋을 해볼까요.
-
73:07 - 73:09
-
73:10 - 73:12
-
73:15 - 73:18
-
73:19 - 73:27TripDAO injected into TripService
-
73:28 - 73:31됐습니다.
-
73:31 - 73:32이런 식으로
-
73:33 - 73:35레거시 코드를 안전하게 리팩토링 할 수 있습니다.
-
73:35 - 73:37레거시 코드를 안전하게 리팩토링 할 수 있습니다.
-
73:39 - 73:41처음 TripService 코드가
-
73:47 - 73:50여기에 있을 텐데요.
-
73:50 - 73:54처음에는 이런 코드로 시작했던 겁니다.
-
73:54 - 74:03처음에는 이런 코드로 시작했던 겁니다.
-
74:03 - 74:06지금은
-
74:06 - 74:09스크린 캐스트를 녹화하고 있으니
-
74:09 - 74:13한 시간 정도 걸렸는데요
-
74:13 - 74:16여기에 사용된 기법들이 익숙한 경우에는요
-
74:16 - 74:20저 같은 경우에는 평소에는 20분 정도 걸립니다.
-
74:20 - 74:22
-
74:22 - 74:24뭔가 새로운 기술을 알게 되면
-
74:24 - 74:26연습을 하세요
-
74:26 - 74:29능숙하게 사용할 때 까지요
-
74:29 - 74:32다시 한 번 말씀 드리면
-
74:32 - 74:35레거시 코드에 작업을 할 때에는
-
74:35 - 74:40깊은 부분부터 작업을 하면 안됩니다.
-
74:40 - 74:42항상 얕은 부분부터 시작을 하세요.
-
74:42 - 74:44얕은 부분에서 시작해서 깊은 부분으로요.
-
74:44 - 74:47얕은 부분에서 테스트를 작성하기 시작해야 합니다
-
74:47 - 74:51그러면 작성하는 테스트로 제품 코드를 이해해갈 수 있고
-
74:51 - 74:55테스트 데이터를 만들어 가면서
-
74:55 - 74:58점점 더 깊은 부분을 테스트할 수 있게 됩니다.
-
74:58 - 75:00반대로
-
75:00 - 75:01리팩토링을 할 때에는
-
75:01 - 75:03가장 깊은 부분부터 시작합니다.
-
75:03 - 75:05깊은 부분부터 메소드를 추출하고
-
75:05 - 75:08행위를 다른 클래스로 옮기다 보면
-
75:08 - 75:11어느 순간부터 코드가
-
75:11 - 75:14줄어들기 시작할 겁니다.
-
75:14 - 75:17
-
75:19 - 75:24읽기 좋고 관리하기 좋은 코드를 작성하세요.
-
75:24 - 75:26도메인 언어가 코드에 사용되도록 하세요.
-
75:26 - 75:28코드에 녹아들 게 하세요.
-
75:28 - 75:30
-
75:30 - 75:32
-
75:32 - 75:37도메인 언어가 테스트와 제품 코드에 녹아들 게 하세요
-
75:37 - 75:43BA와 이야기하는 용어가 테스트 코드 제품 코드에도 그대로 사용되게 하세요.
-
75:43 - 75:46심플한 것을 추구하고
-
75:46 - 75:49사용하는 프레임워크나 단축키 등도 알아두세요
-
75:49 - 75:51신속하게 작업하도록 노력하고
-
75:52 - 75:56개발 환경에도 공을 들여서 단축키 등으로 간단하게
-
75:56 - 75:58메소드나 클래스를 만들거나
-
75:58 - 76:00리팩토링을 할 수 있어야 합니다.
-
76:00 - 76:04항상 조금씩 점진적으로 작업하고요
-
76:04 - 76:05자주 커밋하세요.
-
76:05 - 76:07Git같은 툴을 사용하면
-
76:08 - 76:10실수를 하더라도
-
76:10 - 76:13Ctrl+Z 나 git reset hard 등으로
-
76:13 - 76:15이 전 커밋으로 돌아갈 수 있습니다.
-
76:15 - 76:17문제가 없었던 상태로요.
-
76:17 - 76:19용기를 가지세요.
-
76:19 - 76:22오늘 여러 가지 기법들을 배웠는데요
-
76:22 - 76:27오늘 여러 가지 기법들을 배웠는데요
-
76:27 - 76:31이제 레거시 코드의 99%에 테스트 작성이 가능할 겁니다.
-
76:31 - 76:33그러니 용기를 내서
-
76:33 - 76:36오늘 배운 것들을 활용하세요.
-
76:37 - 76:40머문 자리는 머물기 전보다 깨끗하게 하고 떠나세요.
-
76:40 - 76:41보이 스카웃 규칙이지요.
-
76:41 - 76:43깨진 창문 이론도 아시지요.
-
76:43 - 76:44코드 냄새가 나면요.
-
76:44 - 76:48코드가 그다지 잘 작성된 게 아니라면
-
76:48 - 76:49바로 리팩토링 하세요.
-
76:49 - 76:51바로 수정하세요.
-
76:51 - 76:53유익한 시간이 됐으면 좋겠습니다.
-
76:54 - 76:56이상입니다.
-
76:56 - 77:01이 카타는 제 깃헙에 있습니다.
-
77:01 - 77:04제 깃헙을 방문해보세요.
-
77:04 - 77:06이상입니다. 유익한 시간이 되었으면 좋겠습니다.
-
77:06 - 77:07감사합니다.
![]() |
YongWook Kim edited Korean subtitles for Testing and Refactoring Legacy Code | |
![]() |
YongWook Kim edited Korean subtitles for Testing and Refactoring Legacy Code | |
![]() |
YongWook Kim edited Korean subtitles for Testing and Refactoring Legacy Code | |
![]() |
YongWook Kim edited Korean subtitles for Testing and Refactoring Legacy Code | |
![]() |
YongWook Kim edited Korean subtitles for Testing and Refactoring Legacy Code | |
![]() |
YongWook Kim edited Korean subtitles for Testing and Refactoring Legacy Code | |
![]() |
YongWook Kim edited Korean subtitles for Testing and Refactoring Legacy Code | |
![]() |
YongWook Kim edited Korean subtitles for Testing and Refactoring Legacy Code |