문제

https://programmers.co.kr/learn/courses/30/lessons/42588


코드

본인의 코드가 아닌 해당 문제 최다 좋아요를 받은 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public int[] solution(int[] heights) {
Stack<Tower> st = new Stack<>();
int[] answer = new int[heights.length];

for (int i = 0; i < heights.length; i++) {
Tower tower = new Tower(i + 1, heights[i]);
int receiveIdx = 0;

while (!st.isEmpty()) {
Tower top = st.peek();

if (top.height > tower.height) {
receiveIdx = top.idx;
break;
}

st.pop();
}

st.push(tower);
answer[i] = receiveIdx;
}

return answer;
}

class Tower {
int idx;
int height;

public Tower(int idx, int height) {
this.idx = idx;
this.height = height;
}
}

본인의 코드가 아닌 해당 문제 최다 좋아요를 받은 코드


흐름

  1. 탑의 index와 높이를 저장 할 Tower class 생성
  2. 탑의 개수만큼 반복
  3. 1 번쨰부터 시작되야하므로 현재 index에서 +1 한 index와 현재 높이를 갖는 tower를 생성
  4. tower가 저장된 stack이 비었는지 체크
  5. 비어 있지 않다면 stack의 맨 위를 가져와서 현재 tower의 높이와 비교함
  6. stack에 저장된 stack이 더 크다면 현재 타워에 전파를 받을 타워이므로 index를 receiveIdx 변수에 할당
  7. 크지 않다면 stack에 저장된 tower를 pop함
  8. stack에 현재 tower를 push
  9. answer에 아까 저장한 receiveIdx를 저장
  10. answer return

결과

번호 속도
테스트 1 통과 (1.89ms, 50.7MB)
테스트 2 통과 (1.84ms, 52.6MB)
테스트 3 통과 (1.96ms, 52.3MB)
테스트 4 통과 (2.04ms, 52.5MB)
테스트 5 통과 (2.02ms, 50.8MB)
테스트 6 통과 (2.28ms, 52.7MB)
테스트 7 통과 (1.78ms, 52.6MB)
테스트 8 통과 (2.06ms, 52.1MB)

테스트 케이스

1
2
3
assertArrayEquals(new int[] {0,0,2,2,4}, test.solution(new int[] {6,9,5,7,4}));
assertArrayEquals(new int[] {0,0,0,3,3,3,6}, test.solution(new int[] {3,9,9,3,5,7,2}));
assertArrayEquals(new int[] {0,0,2,0,0,5,6}, test.solution(new int[] {1,5,3,6,7,6,5}));

댓글 공유

문제

https://programmers.co.kr/learn/courses/30/lessons/42585


코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public int solution(String arrangement) {
char[] ch = arrangement.toCharArray();

int answer = 0;
char pre = 0;
Stack<Character> stack = new Stack<Character>();
for (char c : ch) {
if (c == '(') {
stack.push(c);
} else {
stack.pop();
if (pre == c) {
answer += 1;
} else {
answer += stack.size();
}
}

pre = c;
}

return answer;
}

흐름

  1. 문자열을 char array로 변경
  2. char array를 반복하면서 문자가 ‘(‘ 인지 비교
  3. 맞으면 새 막대기가 시작되야 하므로 stack에 push 함
  4. 아니라면 레이저를 발사해서 stack을 잘라야 하므로 pop 하는데
  5. 이전 pop한 문자가 똑같이 ‘)’ 이라면 하나만 더 짤린 것이므로 +1 만하고
  6. 이전 문자가 ‘)’이 아니라면 stack의 크기가 막대의 개수 이므로 stack size만큼 더함
  7. 막대를 하나만 추가할 지 판단하기 위해 현재 문자를 저장함
  8. 문자열을 모두 반복 하면 끝

결과

번호 속도
테스트 1 통과 (19.66ms, 51.2MB)
테스트 2 통과 (1.24ms, 53MB)
테스트 3 통과 (19.95ms, 51MB)
테스트 4 통과 (2.78ms, 53.1MB)
테스트 5 통과 (4.04ms, 53MB)
테스트 6 통과 (3.95ms, 52.8MB)
테스트 7 통과 (14.81ms, 52.5MB)
테스트 8 통과 (16.84ms, 52.6MB)
테스트 9 통과 (17.09ms, 52.8MB)
테스트 10 통과 (16.32ms, 52.6MB)
테스트 11 통과 (13.59ms, 52.2MB)
테스트 12 통과 (14.53ms, 50.6MB)
테스트 13 통과 (15.35ms, 52MB)
테스트 14 통과 (17.90ms, 50.4MB)
테스트 15 통과 (18.95ms, 52.3MB)
테스트 16 통과 (20.62ms, 50.7MB)
테스트 17 통과 (20.66ms, 53.1MB)
테스트 18 통과 (18.49ms, 52.8MB)
테스트 19 통과 (19.85ms, 53MB)
테스트 20 통과 (18.93ms, 52.5MB)

테스트 케이스

1
2
assertEquals(17, test.solution("()(((()())(())()))(())"));
assertEquals(18, test.solution("(((((((((())))))))))"));

댓글 공유

문제

https://programmers.co.kr/learn/courses/30/lessons/43165


코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int solution(int[] numbers, int target) {
int answer = dfs(numbers, target, 0, 0);
return answer;
}

private int dfs(int[] numbers, int target, int depth, int sum) {
if (depth == numbers.length) {
return sum == target ? 1 : 0;
}

int left = dfs(numbers, target, depth + 1, sum + numbers[depth]);
int right = dfs(numbers, target, depth + 1, sum - numbers[depth]);

return left + right;
}

흐름

  • DFS 알고리즘 (재귀) 으로 해결
  • array index를 depth로 치고 모든 경우의 수를 탐색
  1. 0 번째 인덱스부터 시작
  2. depth가 array length와 같으면 제일 깊은 곳 까지 모두 탐색한 것이므로
  3. 합이 target과 같은 지 판단해서 같으면 1, 다르면 0을 return
  4. depth가 length와 다르면, 더 깊이 들어가야 하므로 dfs() 메서드를 depth + 1 하고 해당 깊이 까진 더 한 값을 파라미터로 넘김
  5. 2번부터 다시 반복

마지막 return 부연 설명

1
2
3
if (depth == numbers.length) {
return sum == target ? 1 : 0;
}

트리

  1. 맨 아래에서 맨 왼쪽 1+1+1+1+1은 3이 아니므로 0
  2. 그 옆 1+1+1+1-1은 3이므로 1 return
  3. 그럼 그 윗 노드인 1+1+1+1은 1
  4. 다음 탐색 순서인 1+1+1-1+1은 3이므로 1 return
  5. 1+1+1-1-1 = 1 이므로 0 return
  6. 이들의 부모 노드인 1+1+1-1은 1
  7. 그 위 부모 노드는 1 + 1 이므로 2가 됨
  8. 이런 식으로 마지막 까지 올라가면 결국 +1에선 4, -1 에선 1 되서 정답인 5를 return하게 되고
  9. 실제로 양수로 시작하는 부분에선 4개의 경우가 3이 return 되고, 음수로 시작하는 경우에는 1이 return 되므로 정답임을 알 수 있다.

결과

번호 속도
테스트 1 통과 (6.69ms, 54.4MB)
테스트 2 통과 (6.56ms, 52.9MB)
테스트 3 통과 (0.97ms, 52.8MB)
테스트 4 통과 (1.26ms, 50.9MB)
테스트 5 통과 (1.75ms, 50.1MB)
테스트 6 통과 (1.00ms, 52.3MB)
테스트 7 통과 (0.93ms, 52.5MB)
테스트 8 통과 (1.44ms, 52.6MB)

테스트 케이스

1
2
3
4
assertEquals(5, test.solution(new int[] {1,1,1,1,1}, 3));
assertEquals(3, test.solution(new int[] {2,3,5,7,9}, 2));
assertEquals(1, test.solution(new int[] {1}, 1));
assertEquals(5, test.solution(new int[] {6,2,3,6,7,1}, 7));

참고 사이트

댓글 공유

문제

https://programmers.co.kr/learn/courses/30/lessons/1829


코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
public int[] solution(int m, int n, int[][] picture) {
int numberOfArea = 0;
int maxSizeOfOneArea = 0;

boolean[][] isPassedArea = new boolean[m][n];

for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {

if (picture[i][j] == 0 || isPassedArea[i][j]) {
continue;
}

int areaRange = breadthFirstSearch(i, j, picture, isPassedArea);
maxSizeOfOneArea = (maxSizeOfOneArea < areaRange) ? areaRange : maxSizeOfOneArea;
++numberOfArea;
}
}

int[] answer = new int[2];
answer[0] = numberOfArea;
answer[1] = maxSizeOfOneArea;

return answer;
}

private int breadthFirstSearch(int x, int y, int[][] picture, boolean[][] isPassedArea) {
final int[] xAround = new int[]{1, -1, 0, 0};
final int[] yAround = new int[]{0, 0, 1, -1};

int areaRange = 1;

Queue<Position> queue = new LinkedList<Position>();

setPassedArea(isPassedArea, queue, x, y);

while (!queue.isEmpty()) {
Position currentPosition = queue.poll();

for (int i = 0; i < xAround.length; i++) {
int moveX = xAround[i] + currentPosition.x;
int moveY = yAround[i] + currentPosition.y;

if (!isSameAreaValidation(moveX, moveY, picture, isPassedArea, currentPosition)) {
continue;
}

setPassedArea(isPassedArea, queue, moveX, moveY);
++areaRange;
}
}

return areaRange;
}

private boolean isSameAreaValidation(int moveX, int moveY, int[][] picture, boolean[][] isPassedArea, Position currentPosition) {
if (isOutOfPicture(moveX, moveY, picture)) {
return false;
}

if (picture[moveX][moveY] == 0) {
return false;
}

if (isPassedArea[moveX][moveY]) {
return false;
}

if (picture[currentPosition.x][currentPosition.y] != picture[moveX][moveY]) {
return false;
}

return true;
}

private boolean isOutOfPicture(int moveX, int moveY, int[][] picture) {
if (moveX < 0 || moveY < 0) {
return true;
}

if (picture.length <= moveX || picture[0].length <= moveY) {
return true;
}

return false;
}

private void setPassedArea(boolean[][] isPassedArea, Queue<Position> queue, int x, int y) {
isPassedArea[x][y] = true;
queue.offer(new Position(x, y));
}

class Position {
private int x = 0;
private int y = 0;

public Position(int x, int y) {
this.x = x;
this.y = y;
}
}

흐름

  1. 이미 지나온 배열인지 확인하기 위해 picture array와 똑같은 크기의 boolean array를 만듦
  2. 2차원 배열 크기만큼 돌면서 picture[i][j] value가 0이거나 이미 지나온 길이면 찾아갈 필요가 없으므로 continute 해서 길을 찾지 않음
  3. 아니면 좌표 x,y를 저장할 class를 담는 Queue를 생성함
  4. queue에 현재 좌표를 저장하고 boolean array[x][y]를 true로 할당
  5. queue에 있는 값을 하나씩 꺼내면서 queue가 전부 비워질 때까지 반복
  6. queue에서 꺼낸 현재 좌표에서 상하 좌우를 비교해서 같은지 판단해야 하므로 현재 좌표에서 x -1 , +1, y +1, -1 씩 돌아가면서 판단할 좌표를 구함
  7. 구한 좌표가 validation을 통과하면 4번과 마찬가지로 좌표를 queue에 저장하고 true로 변경
    1. 구한 좌표 x, y가 array 범위를 넘어가진 않는 지
    2. 구한 좌표의 value가 0이 아닌 값 인지
    3. 구한 좌표가 이미 지나간 길이 아닌지
    4. 구한 좌표의 value가 현재 좌표의 value와 같은 지
    5. 위 4가지를 모두 통과해야 validation 통과
  8. 현재 영역의 크기를 증가시킴
  9. queue가 전부 비워지면 영역의 크기를 return해서 이전에 구한 영역의 크기와 비교해서 큰 값을 저장
  10. 영역 개수를 증가시킴

결과

결과


테스트 케이스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int[][] picture = new int[][] {{1, 1, 1, 0}, {1, 2, 2, 0}, {1, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 3}, {0, 0, 0, 3}};
assertArrayEquals(new int[] {4 , 5}, test.solution(6, 4, picture));

int[][] picture1 = new int[][] {{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}};
assertArrayEquals(new int[] {0 , 0}, test.solution(5, 5, picture1));

int[][] picture2 = new int[][] {{1, 0, 0, 0, 0}, {0, 0, 1, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 1}};
assertArrayEquals(new int[] {3 , 1}, test.solution(5, 5, picture2));

int[][] picture3 = new int[][] {{1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}};
assertArrayEquals(new int[] {1 , 25}, test.solution(5, 5, picture3));

int[][] picture4 = new int[][] {{1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 100, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}};
assertArrayEquals(new int[] {2 , 24}, test.solution(5, 5, picture4));

int[][] picture5 = new int[][] {{1, 1, 1, 0}, {1, 1, 1, 0}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 1}};
assertArrayEquals(new int[] {2 , 6}, test.solution(6, 4, picture5));

int[][] picture6 = new int[][] {{0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0}, {0, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 1, 3, 3, 3, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 0}, {0, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 0}, {0, 0, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 0, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}};
assertArrayEquals(new int[] {12 , 120}, test.solution(13, 16, picture6));

참고 사이트

댓글 공유

문제

Test Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Test
void testCreateWordFail() throws Exception {
// gvien
BindingResult bindingResult = new MapBindingResult(new HashMap(), "");
bindingResult.rejectValue("test", "test", "haha");

WordDTO.AddWord addWord = new WordDTO.AddWord();
addWord.setWord("test");
addWord.setMeaning("테스트");

given(this.service.getCreateWordBindingResult(addWord, bindingResult)).willReturn(bindingResult);

String addWordToJson = getAddWordToJson();

//when
ResultActions action = mockMvc.perform(post("/word/add")
.param("save", "")
.content(addWordToJson)
.contentType(MediaType.APPLICATION_JSON)
.with(csrf()))
.andDo(print());

//then
action.andExpect(status().isOk())
.andExpect(view().name("thymeleaf/word/createWordForm"));
}

위 코드처럼 this.service.getCreateWordBindingResult(addWord, bindingResult) method가 호출될 때 willdReturn으로 지정한 bindingResult가 return되게 하고 하려했으나 given이 제대로 동작하지 않아서 인지 getCreateWordBindingResult method 안에서 result가 존재하지 않아 NullPointerException이 발생하여 테스트가 실패했다.

Test Target

1
2
3
4
5
6
7
8
9
10
11
@PostMapping(value="/word/add", params= {"save"})
public String createWord(@Valid AddWord word, BindingResult bindingResult) {
BindingResult result = service.getCreateWordBindingResult(word, bindingResult);

if (result.hasErrors()) { // NullPointerException 발생
return "thymeleaf/word/createWordForm";
}

service.insertWord(word);
return "thymeleaf/index";
}

해결

ArgumentMatchers class에 any()를 이용해서 파라미터를 넘겼더니 정상적으로 동작했다.

1
given(this.service.getCreateWordBindingResult(any(WordDTO.AddWord.class), any(BindingResult.class))).willReturn(bindingResult);
  • p.s : 기본 자료형 (int, char)이나 String 등은 any()를 사용하지 않고 그대로 파라미터로 받아도 정상적으로 동작하는 것 같은데 어째서 따로 정의한 클래스만 인식(?) 하지 못해서 willReturn이 먹히지 않고 null이 return 되는지 알 수가 없었다.

전체 코드

댓글 공유

문제

Spring Boot + thymeleaf으로 진행하던 프로젝트를 테스트 하던 중
Ajax로 호출하는 Methoed를 Test 하였더니 아래와 같이 403 error가 return 되면서 Test가 실패했다.


원인

thymeleaf를 사용하면 기본적으로 CSRF 토큰을 넘겨주기 때문에 따로 csrf 토큰을 생성하지 않았으나, ajax에선 직접 csrf 토큰을 생성해서 넘겨줘야 하기 때문에 csrf 토큰을 생성해서 넘겨주도록 되어 있는데 Test 시에는 그렇지 않아서 403 Forbidden가 발생한 것으로 보인다.

403

thymeleaf config.html

1
2
<meta id="_csrf" name="_csrf" th:content="${_csrf.token}"/>
<meta id="_csrf_header" name="_csrf_header" th:content="${_csrf.headerName}"/>

common.js

1
2
const token = $("meta[name='_csrf']").attr("content");
const header = $("meta[name='_csrf_header']").attr("content");

해결

mockMvc.perform에서 get, post 호출 시 SecurityMockMvcRequestPostProcessors 의 csrf()with()로 파라미터에 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;

...

//when
ResultActions action = mockMvc.perform(post("/word/add")
.param("save", "")
.content(addwordToJson)
.contentType(MediaType.APPLICATION_JSON)
.with(csrf())) // 이 부분
.andDo(print());

...

추가 한 후 다시 테스트 해보면 파라미터에 csrf 토큰이 생성되어 정상적으로 동작하는 것을 확인 할 수 있다.

1
2
3
4
5
6
7
8
9
10
MockHttpServletRequest:
HTTP Method = POST
Request URI = /word/add
Parameters = {save=[], _csrf=[6757e021-c0a3-4390-81d9-9c917d89c8c1]} // 이 부분
Headers = [Content-Type:"application/json", Content-Length:"59"]
Body = <no character encoding set>
Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@83a38fdf: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@83a38fdf: Principal: org.springframework.security.core.userdetails.User@31bf3c: Username: jgji; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER}

...

성공


참고 사이트


전체 코드

댓글 공유

Codedeploy에서 배포그룹 생성 시 인스턴스를 잘못 등록

1
2
The deployment failed because no instances were found for your deployment group.
Check your deployment group settings to make sure the tags for your Amazon EC2 instances or Auto Scaling groups correctly identify the instances you want to deploy to, and then try again.

CodeDeploy

배포 그룹에서 태그 그룹으로 인스턴스를 지정할 때 위 사진처럼 일치하는 인스턴스가 존재한다는 문구가 나타나는 걸 확인해야한다.


생성한 버킷 리전과 인스턴스의 리전이 틀려서 에러 발생

1
2
payload:"{\"error_code\":5,\"script_name\":\"\",\"message\":\"
The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.\",\"log\":\"\"}"}

위 에러 로그는 아래 디렉토리에 위치한 파일을 열어서 확인 할 수 있다.
shell script에서 작성한 echo도 아래 파일에서 함께 확인 할 수 있다.

1
vim /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log

CodeDeploy Log File

댓글 공유

에러

Travis 권한 에러

1
./mvnw: Permission denied

Spring Boot + Maven 프로젝트 Travis CI 연동 중 발생한 에러


해결

1
2
before_install:
- chmod +x mvnw

설정으로 권한 부여하여 해결

전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
language: java
jdk:
- openjdk8

branches:
only:
- master

# Travis CI 서버의 Home
cache:
directories:
- $HOME/.m2

before_install:
- chmod +x mvnw

# MAVEN 프로젝트는 필요 없음
#script: "./mvnw package"

# CI 실행 완료시 메일로 알람
notifications:
email:
recipients:
- your@gmail.com

참고 사이트

댓글 공유

Junggu Ji

author.bio


author.job