문제
https://programmers.co.kr/learn/courses/30/lessons/17680
코드
1 | private static final int MISS_RUN_TIME = 5; |
흐름
- 캐시가 0인 경우엔 캐시에 저장 할 수 없으니 항상 miss 이므로 배열의 크기 만큼 5를 곱해서 return 한다.
- LinkedHashMap의 경우 accessOrder parameter를 통해 Map의 순서를 access 기준으로 변경 할 수 있는데 우리는 LRU, 즉 가장 오래 사용되지 않은 데이터를 삭제 해야하므로 accessOrder를 true로 갖는 생성자로 LinkedHashMap 인스턴스를 생성한다.
- 그 후 removeEldestEntry()를 override해서 캐시의 크기보다 Map의 사이즈가 커질 경우 가장 오래 사용되지 않은 데이터를 삭제하도록 수정한다.
- 배열에서 문자열을 가져오면서 Map에 저장되어 있는지 비교한다.
- Map에 저장되어 있다면, get()을 통해 access하고 1초를 더한다.
- 저장되어 있지 않다면, put 하는데 이 때 Map의 크기가 캐시보다 크다면 아까 Override 한 removeEldestEntry()에 의해 가장 오래 사용되지 않은 값이 삭제되면서 지금 put한 데이터가 추가한다.
- 캐시에 없는 데이터를 추가했으니 5초를 더한다.
- 모든 초를 더한 값을 return 한다.
결과
번호 | 속도 |
---|---|
테스트 1 | 통과 (1.11ms, 52.1MB) |
테스트 2 | 통과 (1.20ms, 52.2MB) |
테스트 3 | 통과 (1.08ms, 50.9MB) |
테스트 4 | 통과 (1.23ms, 51.7MB) |
테스트 5 | 통과 (0.94ms, 52.4MB) |
테스트 6 | 통과 (0.78ms, 50.2MB) |
테스트 7 | 통과 (0.78ms, 52.2MB) |
테스트 8 | 통과 (1.12ms, 52.7MB) |
테스트 9 | 통과 (1.15ms, 50.8MB) |
테스트 10 | 통과 (1.34ms, 52.4MB) |
테스트 11 | 통과 (45.53ms, 89.6MB) |
테스트 12 | 통과 (1.45ms, 52.5MB) |
테스트 13 | 통과 (1.43ms, 52.5MB) |
테스트 14 | 통과 (1.69ms, 52.7MB) |
테스트 15 | 통과 (2.42ms, 50.5MB) |
테스트 16 | 통과 (2.85ms, 52.9MB) |
테스트 17 | 통과 (0.78ms, 52MB) |
테스트 18 | 통과 (4.34ms, 53.5MB) |
테스트 19 | 통과 (4.28ms, 53.3MB) |
테스트 20 | 통과 (4.15ms, 50.9MB) |
테스트 케이스
1 | assertEquals(42, test.solution(3, new String[]{"Jeju", "Pangyo", "Jeju", "NewYork", "LA", "Jeju", "Pangyo", "Seoul", "NewYork", "LA"})); |
참고 사이트
문제
https://programmers.co.kr/learn/courses/30/lessons/42842
코드
1 | public int[] solution(int brown, int yellow) { |
흐름
- 갈색과 노란색을 더해 전체 크기를 구한다.
- 전체 크기를 2부터 반복한다. (1인 경우는 없으므로)
- i로 sum을 나눠서 떨어진다면, 즉 약수라면
- sum에서 i를 나눈 값에 2를 곱해서 위 아래 가로의 길이를 구하고
- i에 마찬가지로 2를 곱해서 좌 우 세로의 길이를 구하고
- 꼭지점들은 가로 세로가 겹치므로 -4를 한다.
- 이렇게 구한 갈색을 sum에서 빼서 노란색의 값을 구한다.
- 구한 갈색과 노란색 값이 넘겨받은 brown과 yellow와 같으면 끝
- 아니라면 반복한다.
- 끝
결과
번호 | 속도 |
---|---|
테스트 1 | 통과 (1.57ms, 52.3MB) |
테스트 2 | 통과 (1.76ms, 51.1MB) |
테스트 3 | 통과 (1.88ms, 50.1MB) |
테스트 4 | 통과 (1.44ms, 52.7MB) |
테스트 5 | 통과 (1.52ms, 50.5MB) |
테스트 6 | 통과 (1.48ms, 49.8MB) |
테스트 7 | 통과 (1.62ms, 50.8MB) |
테스트 8 | 통과 (1.52ms, 52.6MB) |
테스트 9 | 통과 (1.58ms, 52.4MB) |
테스트 10 | 통과 (1.58ms, 50.4MB) |
테스트 11 | 통과 (1.53ms, 53MB) |
테스트 12 | 통과 (1.56ms, 52.3MB) |
테스트 13 | 통과 (1.48ms, 53.3MB) |
테스트 케이스
1 | assertArrayEquals(new int[]{4, 3}, test.solution(10, 2)); |
문제
https://programmers.co.kr/learn/courses/30/lessons/42888
코드
1 |
|
흐름
- 문자열을 공백으로 나눠서 행동과 유저의 아이디를 저장하는 배열에 담는다.
- 행동이 Change가 아닌 경우엔 채팅방에 입장했거나 나갓다는 문구룰 출력해야 하기 때문에 List에 담는다.
- 행동이 Leave가 아닌 경우엔 닉네임이 변경되었거나, 새로 방에 들어온 것이므로 유저 아이디를 key로하는 map에 value로 닉네임을 저장한다.
- 모두 저장했으면 List만큼 돌면서 해당 행동에 맞는 문자열을 만들어 저장한다.
- 끝
결과
번호 | 속도 |
---|---|
테스트 1 | 통과 (18.70ms, 54.1MB) |
테스트 2 | 통과 (17.35ms, 52.4MB) |
테스트 3 | 통과 (17.84ms, 54.4MB) |
테스트 4 | 통과 (19.81ms, 54MB) |
테스트 5 | 통과 (27.75ms, 56.1MB) |
테스트 6 | 통과 (30.38ms, 56.1MB) |
테스트 7 | 통과 (35.26ms, 54.4MB) |
테스트 8 | 통과 (27.59ms, 55.2MB) |
테스트 9 | 통과 (30.72ms, 56.5MB) |
테스트 10 | 통과 (28.67ms, 56.1MB) |
테스트 11 | 통과 (25.16ms, 56.2MB) |
테스트 12 | 통과 (26.04ms, 56.6MB) |
테스트 13 | 통과 (26.07ms, 55MB) |
테스트 14 | 통과 (43.84ms, 57.2MB) |
테스트 15 | 통과 (20.35ms, 52.4MB) |
테스트 16 | 통과 (18.03ms, 52.8MB) |
테스트 17 | 통과 (19.77ms, 54.2MB) |
테스트 18 | 통과 (18.86ms, 52.5MB) |
테스트 19 | 통과 (35.94ms, 56.7MB) |
테스트 20 | 통과 (26.22ms, 55.5MB) |
테스트 21 | 통과 (30.18ms, 56.5MB) |
테스트 22 | 통과 (28.53ms, 52.5MB) |
테스트 23 | 통과 (29.40ms, 57.5MB) |
테스트 24 | 통과 (37.73ms, 57.3MB) |
테스트 25 | 통과 (262.95ms, 158MB) |
테스트 26 | 통과 (292.57ms, 158MB) |
테스트 27 | 통과 (333.29ms, 169MB) |
테스트 28 | 통과 (324.94ms, 163MB) |
테스트 29 | 통과 (282.87ms, 157MB) |
테스트 30 | 통과 (286.21ms, 153MB) |
테스트 31 | 통과 (225.37ms, 147MB) |
테스트 32 | 통과 (255.96ms, 137MB) |
테스트 케이스
1 | String[] actual = {"Enter uid1234 Muzi", "Enter uid4567 Prodo","Leave uid1234","Enter uid1234 Prodo","Change uid4567 Ryan"}; |
문제
https://programmers.co.kr/learn/courses/30/lessons/12949
코드
1 | public int[][] solution(int[][] arr1, int[][] arr2) { |
흐름
- 행렬의 곱은 앞 행렬의 행과 뒤 행렬의 열을 곱한 것과 같다.
- ex) 앞 행렬이 m x n 크기이고 뒤 행렬이 n x r 크기인 경우 곱은 m x r크기의 행렬이 된다.
- 곱 행렬(0, 0)은 앞 행렬의 (0, 0) X 뒷 행렬 (0, 0) + 앞 행렬 (0, 1) X 뒷 행렬 (1, 0)
- (0, 1)은 (0, 0) X (0, 1) + (0, 1) X (1, 1)
- (1, 0)은 (1, 0) X (0, 0) + (1, 1) X (0, 1)
- (1, 1)은 (1, 0) X (0, 1) + (1, 1) X (1, 1)
- (2, 0)은 (2, 0) X (0, 0) + (2, 1) X (0, 1)
- (2, 1)은 (2, 0) X (0, 1) + (2, 1) X (1, 1)
- 이런식으로 진행된다.
결과
번호 | 속도 |
---|---|
테스트 1 | 통과 (2.93ms, 54.1MB) |
테스트 2 | 통과 (10.09ms, 54.3MB) |
테스트 3 | 통과 (17.85ms, 56.7MB) |
테스트 4 | 통과 (2.02ms, 50.9MB) |
테스트 5 | 통과 (8.78ms, 53.7MB) |
테스트 6 | 통과 (9.40ms, 56.2MB) |
테스트 7 | 통과 (1.56ms, 52.3MB) |
테스트 8 | 통과 (1.80ms, 50.7MB) |
테스트 9 | 통과 (1.79ms, 50.1MB) |
테스트 10 | 통과 (8.66ms, 54.9MB) |
테스트 11 | 통과 (3.37ms, 52.3MB) |
테스트 12 | 통과 (2.05ms, 54.6MB) |
테스트 13 | 통과 (9.96ms, 54.1MB) |
테스트 14 | 통과 (10.60ms, 54.3MB) |
테스트 15 | 통과 (7.94ms, 54.1MB) |
테스트 16 | 통과 (4.86ms, 54.6MB) |
테스트 케이스
1 | assertArrayEquals(new int[][] {{15, 15}, {15, 15}, {15, 15}}, test.solution(new int[][] {{1, 4}, {3, 2}, {4, 1}}, new int[][] {{3, 3}, {3, 3}})); |
참고 사이트
문제
https://programmers.co.kr/learn/courses/30/lessons/12913
코드
1 | int solution(int[][] land) { |
흐름
- 마지막 행 전행 부터 반복문을 시작해서 i가 0이 될 때까지 반복한다.
- i행의 각 인덱스에는 자기 자신의 열을 제외한 이전 행의 값 중 가장 큰 값과 자기 자신의 값을 더한 값을 저장한다.
- 위 과정을 반복하면 0번째 행들의 index에 계산된 값들이 할당되어 있을 테니
- 0 번째 행 중에서 가장 큰 값을 return한다.
- 끝
결과
정확성
번호 | 속도 |
---|---|
테스트 1 | 통과 (1.05ms, 51.1MB) |
테스트 2 | 통과 (1.16ms, 52.7MB) |
테스트 3 | 통과 (1.16ms, 50.8MB) |
테스트 4 | 통과 (1.18ms, 53MB) |
테스트 5 | 통과 (1.18ms, 52.7MB) |
테스트 6 | 통과 (1.14ms, 52.9MB) |
테스트 7 | 통과 (1.18ms, 51.4MB) |
테스트 8 | 통과 (1.13ms, 52.7MB) |
테스트 9 | 통과 (1.25ms, 53.3MB) |
테스트 10 | 통과 (1.21ms, 50.7MB) |
테스트 11 | 통과 (1.18ms, 52.4MB) |
테스트 12 | 통과 (4.25ms, 52.5MB) |
테스트 13 | 통과 (1.19ms, 53.2MB) |
테스트 14 | 통과 (1.16ms, 53.1MB) |
테스트 15 | 통과 (1.19ms, 51MB) |
테스트 16 | 통과 (1.16ms, 52.5MB) |
테스트 17 | 통과 (1.14ms, 55.3MB) |
테스트 18 | 통과 (1.16ms, 52.2MB) |
효율성
번호 | 속도 |
---|---|
테스트 1 | 통과 (28.10ms, 94.3MB) |
테스트 2 | 통과 (28.01ms, 95.7MB) |
테스트 3 | 통과 (25.28ms, 97.6MB) |
테스트 4 | 통과 (25.93ms, 101MB) |
테스트 케이스
1 | assertEquals(16, test.solution(new int[][] {{1,2,3,5},{5,6,7,8},{4,3,2,1}})); |
문제
https://programmers.co.kr/learn/courses/30/lessons/67257
코드
1 | private static final char PLUS = '+'; |
흐름
- 수식으로 넘어온 문자열을 숫자와 연산자로 나눈다.
- 연산자들을 중복되지 않게 저장한다.
- 문자열에 포함되어 있는 연산자들을 재귀로 반복하면서 모든 순열을 구한다.
- 0번째와 1번째를 교환하고 0번째와 2번째를 교환하고 하는 식으로 모든 순열을 구한다.
- 연산자의 개수만큼 순열로 만들어서 경우의 수를 구하기 위해 연산자가 2개인 경우엔 2! = 2개, 3개인 경우엔 3! = 6개가 된다.
- 모든 경우의 수를 구했으면 우선 순열의 수 만큼 반복해서 모든 경우의 수의 값을 구해야 하므로 순열의 수 만큼 반복한다.
- 돌면서 숫자를 담은 리스트와 연산자를 담은 리스트의 사이즈와 같은 큐를 만들고 각각 값들을 저장해서 초기화한다.
- 이번에 계산 할 순열(ex) * + -)을 char[]로 나눠서 그 갯수만큼 loop 돈다.
- 돌면서 숫자와 연산자를 저장 할 Stack를 각각 만든다.
- 숫자를 저장한 큐에서 하나를 꺼내서 stack에 저장하고 꺼낸 놈을 다시 큐의 맨 마지막에 저장한다.
- 그 후 큐의 개수만큼 loop 돌면서 큐들에서 하나씩 꺼내고 마찬가지로 스택에 저장한다.
- 그리고 꺼낸 연산자가 지금 계산 해야할 연산자라면 stack들에서 숫자 2개와 연산자를 pop하고 연산자에 맞게 계산해준다.
- 계산 한 값은 뒤에 또 그 값을 다른 값과 계산해야하므로 stack와 queue에 저장한다.
- 그리고 8번에서 큐에 초기값을 먼저 저장해서 큐의 맨 앞값과 큐의 맨 마지막 값이 계산된 것이므로 큐의 맨 마지막 값을 꺼낸다.
- 이번에 계산 할 연산자가 아니라면 지금 꺼낸 값과 연산자는 다시 큐에 저장한다.
- 4번부터 13번 까지를 모두 반복하면 끝
* + - 일 경우 Queue와 Stack의 변화
- 초기화
1 | LinkedList<Long> numberQueue = new LinkedList<>(numberList); |
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
숫자 | 100 | 200 | 300 | 500 | 20 |
연산자 | - | * | - | + |
- 숫자 초기값 저장
1 | long first = numberQueue.poll(); |
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
숫자 | 200 | 300 | 500 | 20 | 100 |
연산자 | - | * | - | + |
숫자 | 연산자 | |
---|---|---|
5 | ||
4 | ||
3 | ||
2 | ||
1 | 100 |
- 이후 반복
1 | for (int i = 0; i < limit; i++) { |
1회 반복
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
숫자 | 300 | 500 | 20 | 100 | 200 |
연산자 | * | - | + | - |
숫자 | 연산자 | |
---|---|---|
5 | ||
4 | ||
3 | ||
2 | 200 | |
1 | 100 | - |
2회 반복
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
숫자 | 500 | 20 | 100 | 60000 | |
연산자 | - | + | - |
숫자 | 연산자 | |
---|---|---|
5 | ||
4 | ||
3 | ||
2 | 60000 | |
1 | 100 |
3회 반복
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
숫자 | 20 | 100 | 60000 | 500 | |
연산자 | + | - | - |
숫자 | 연산자 | |
---|---|---|
5 | ||
4 | ||
3 | 500 | |
2 | 60000 | |
1 | 100 | - |
4회 반복
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
숫자 | 100 | 60000 | 500 | 20 | |
연산자 | - | - | + |
숫자 | 연산자 | |
---|---|---|
5 | ||
4 | 20 | |
3 | 500 | - |
2 | 60000 | + |
1 | 100 | - |
- 이런 식으로 반복되고 queue 크기 만큼 돌았다는건 우선 계산해야 될 연산자를 다 계산 했다는 것이므로 다시 스택을 비우고 처음부터 위 계산을 반복한다.
- 다시 계산해도 큐에 계산 된 값이 저장되어 있으므로 정상적으로 계산된다.
결과
번호 | 속도 |
---|---|
테스트 1 | 통과 (1.22ms, 52.9MB) |
테스트 2 | 통과 (1.25ms, 51.9MB) |
테스트 3 | 통과 (1.54ms, 51.7MB) |
테스트 4 | 통과 (1.62ms, 52.6MB) |
테스트 5 | 통과 (1.70ms, 52.8MB) |
테스트 6 | 통과 (1.73ms, 52.9MB) |
테스트 7 | 통과 (1.69ms, 52.1MB) |
테스트 8 | 통과 (1.76ms, 52.9MB) |
테스트 9 | 통과 (1.81ms, 52.3MB) |
테스트 10 | 통과 (1.86ms, 52.1MB) |
테스트 11 | 통과 (1.70ms, 50.6MB) |
테스트 12 | 통과 (1.90ms, 52.3MB) |
테스트 13 | 통과 (2.62ms, 52.2MB) |
테스트 14 | 통과 (2.17ms, 53.2MB) |
테스트 15 | 통과 (2.15ms, 53.1MB) |
테스트 16 | 통과 (1.36ms, 52.7MB) |
테스트 17 | 통과 (1.57ms, 52.8MB) |
테스트 18 | 통과 (1.28ms, 52.9MB) |
테스트 19 | 통과 (1.30ms, 51.3MB) |
테스트 20 | 통과 (1.33ms, 52.6MB) |
테스트 21 | 통과 (1.53ms, 52.5MB) |
테스트 22 | 통과 (1.47ms, 54.3MB) |
테스트 23 | 통과 (1.21ms, 52.3MB) |
테스트 24 | 통과 (2.11ms, 50.7MB) |
테스트 25 | 통과 (2.17ms, 52.2MB) |
테스트 26 | 통과 (1.39ms, 52.3MB) |
테스트 27 | 통과 (2.70ms, 53MB) |
테스트 28 | 통과 (1.74ms, 52.6MB) |
테스트 29 | 통과 (1.59ms, 50.4MB) |
테스트 30 | 통과 (1.58ms, 50.2MB) |
테스트 케이스
1 | assertEquals(60420, test.solution("100-200*300-500+20")); |
상황
프로덕션 환경에서 Mariadb를 활용하고 있었는데, 프로덕션 환경의 DB로 Repository class를 테스트를 진행하니 테스트 데이터는 롤백되어 DB 남지 않았지만, Auto increment로 지정한 ID가 증가돼서 실제 서비스에서 데이터 저장 시 증가된 ID로 데이터가 저장되는 문제가 발생했다.
해서 Test할 때는 사용 할 데이터베이스 H2를 이용하기로 했다.
H2 데이터베이스는 인메모리 관계형 데이터베이스로 메모리 안에서 실행되기 때문에 어플리케이션을 시작할 때마다 초기화되어 테스트 용도로 많이 사용된다.
하지만 테스트 환경도 프로덕션 환경과 비슷하게 만들어서 테스트 하는 경우에는 테스트환경에도 프로덕션 DB를 생성해서 사용하는 경우도 있다고 한다.
필자가 okky에 올린 질문글
Repository Test시 ID 자동 증가
Test 환경에서 H2 적용
우선 H2 DB를 POM.xml에 추가하여 의존성을 등록한다.
1 | <!-- https://mvnrepository.com/artifact/com.h2database/h2 --> |
그 후 Test 환경에서 사용하는 appication.properties에서 데이터베이스 설정을 H2로 설정해준다.
application-test.properties 위치
application-test.properties 내용
1 | jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1 = |
- 프로덕션 환경에서 Mariadb를 사용하기 때문에 dialect를 Mariadb로 설정하고 MODE=Mysql로 설정했다.
- spring.jpa.hibernate.ddl-auto=none으로 설정하면 시작 시 마다 초기화되지 않기 떄문에 테스트 환경에선 꼭 create-drop으로 설정해준다.
Repository Test class
1 |
|
- JPA Test를 위해 JPA 관련된 설정을 불러오는 @DataJpaTest
- Test환경에선 프로덕션 환경과 다르게 H2 DB를 사용하므로 H2 DB설정을 지정한 application-test.properties를 호출하기 위한 @TestPropertySource
이 정도로 설정하고 Test하면 정상적으로 H2를 이용한 테스트가 성공할 것이다.
여담
1 |
|
위 처럼 @Before 메서드를 지정해놓았는데, 각각 Test 메서드를 실행하였을 땐 Auto increment로 지정한 user의 id가 정상적으로 1, 2 이런식으로 생성되었지만 test class 전체로 test를 실행하니 DB가 메서드 마다 각각 실행하고 초기화 되는 것이 아닌지 User Id가 계속 증가해서 테스트가 실패하는 문제가 있었다.
테스트 시 꼭 메서드 각각으로하고 성공한다고 넘어가는게 아니라 클래스 전체로 테스트를 해봐야 할 것 같다.