💻문제점
오늘 알고리즘 시험에서 3번 문제를 풀 때 첫 번째 테스트 케이스는 잘 수행되었다.
import java.util.Arrays;
public class Test3 {
static int[] dx = {1, -1, 0, 0, 1, 1, -1, -1};
static int[] dy = {0, 0, 1, -1, 1, -1, 1, -1};
static int SIZE;
static char[][] map;
static char[][] resultMap;
public static void solution(int x, int y, char mine) {
if (x < 0 || x >= SIZE || y < 0 || y >= SIZE) {
return;
}
char now = map[x][y];
//지뢰라면
if (now != '.') {
resultMap[x][y] = '*';
//주변 8칸 탐색
for (int i = 0; i < dx.length; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 0 || nx >= SIZE || ny < 0 || ny >= SIZE) continue;
solution(nx, ny, now);
}
} else {
//지뢰이거나, 'M'이면 더이상 계산하지 않음
if (resultMap[x][y] == '*' || resultMap[x][y] == 'M') return;
int sum = (resultMap[x][y] - '0') + (mine - '0');
resultMap[x][y] = countMine(sum);
}
}
public static char countMine(int sum) {
if (sum >= 10) return 'M';
return Character.forDigit(sum, 10);
}
public static void main(String[] args) {
/* 초기화 및 첫 번째 테스트 케이스 입력값 생략 */
System.out.println(Arrays.deepToString(resultMap));
}
}
하지만, 두 번째 테스트 케이스에서 다음과 같이 StackOverFlow가 발생했다.
📃시도
- 초반에 디버깅을 몇 번 해보고 값이 제대로 적용되는 것 같았고, 원인을 찾지 못했다.
- 깊이 우선 탐색을 하지 않고, 아이디어는 비슷하게 for문으로 설계하려고 했다. 이중 배열 map의 각각의 요소가 지뢰이면, 인근 8개의 요소에 접근하는 방식으로... 작성하다보니 코드가 길어지고, 함수로 분리할 필요성을 느끼고 역시 DFS 고쳐서 사용해야 겠다 생각했다.
🔍해결
인내심을 갖고 계속 디버깅을 돌리면서 어떻게 재귀가 호출되는지 꼼꼼히 살폈다. 그러다보니 이미 방문했던 지뢰를 방문해 다시 그 지뢰의 주변 8칸을 방문하는 것을 보고, 이미 방문한 지뢰는 방문하지 않도록 했다.
지뢰가 아닌 칸들은 여러 번 방문해야 하기 때문에 방문 여부를 체크할 필요성을 못 느꼈었는데, 지뢰는 딱 한 번만 방문해야했다.
boolean타입의 isVisited 이중 배열을 선언해서 방문 여부를 확인하도록 했다.
두 번째 테스트 케이스의 결과가 제대로 출력되는 것을 확인할 수 있었다. 휴..
import java.util.Arrays;
public class Test3 {
static int[] dx = {1, -1, 0, 0, 1, 1, -1, -1};
static int[] dy = {0, 0, 1, -1, 1, -1, 1, -1};
static int SIZE;
static char[][] map;
static char[][] resultMap;
static boolean[][] isVisited;
public static void solution(int x, int y, char mine) {
if (x < 0 || x >= SIZE || y < 0 || y >= SIZE) {
return;
}
char now = map[x][y];
//방문하지 않은 지뢰라면
if (now != '.' && !isVisited[x][y]) {
resultMap[x][y] = '*';
isVisited[x][y] = true; //방문 표시
//주변 8칸 탐색
for (int i = 0; i < dx.length; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx < 0 || nx >= SIZE || ny < 0 || ny >= SIZE) continue;
solution(nx, ny, now);
}
} else {
//지뢰이거나, 'M'이면 더이상 계산하지 않음
if (resultMap[x][y] == '*' || resultMap[x][y] == 'M') return;
int sum = (resultMap[x][y] - '0') + (mine - '0');
resultMap[x][y] = countMine(sum);
}
}
public static char countMine(int sum) {
if (sum >= 10) return 'M';
return Character.forDigit(sum, 10);
}
public static void main(String[] args) {
/* 초기화 및 두 번째 테스트 케이스 입력값 생략 */
System.out.println(Arrays.deepToString(resultMap));
}
}
🤦♀️의문점
- 쿠키 사용해본 경험이 있어서 알고 있는데, 세션 쿠키는 처음 들었다. 쿠키와 세션은 뭐가 다르지?
💡알게 된 점
쿠키와 세션
Http는 무상태(Stateless) 특성을 가진다. 때문에 다음과 같은 문제가 발생한다.
로그인 > 로그인이 필요한 페이지로 이동 > 서버는 사용자가 로그인했다는 것을 확인할 방도가 없다!
이러한 특성을 극복하고자 나온 기술이 바로 쿠키와 세션이다.
두 기술은 모두 특정 데이터를 key-value 로 저장한다.
쿠키는 클라이언트에 저장되고, 세션은 서버측에서 생성하여 세션 저장소에 저장된다. (톰캣에서는 JSESSIONID 이라는 이름을 이용해서 세션 쿠키를 생성한다.)
이렇게 생성된 JSESSIONID 쿠키를 브라우저 쿠키에 저장한다.
톰캣 내부의 세션 저장소는 발행된 쿠키들을 저장한다. 새로운 JSESSIONID 쿠키가 생성될 때마다 메모리 공간을 차지해야 하는 문제점이 있다. 때문에 톰캣이 주기적으로 조사하며 더 이상 사용하지 않는 값들을 정리하는 방식으로 동작한다.
이때, session-timeout 설정을 사용하여 지정된 시간보다 오래된 값을 삭제한다.
쿠키
- 이름을 원하는대로 지정 가능
- 유효기간 지정 가능
- 반드시 직접 응답 메시지에 추가해주어야 함
- 경로나 도메인 등 지정 가능
'TIL' 카테고리의 다른 글
[TIL - 20230415] (0) | 2023.04.15 |
---|---|
[TIL - 20230414] 블로그 과제 (0) | 2023.04.15 |
[TIL - 20230411] yml의 DB 정보 .gitignore 등록 (0) | 2023.04.11 |
[TIL - 20230410] DAO, DTO, VO (0) | 2023.04.10 |
[WIL - 20230409] (0) | 2023.04.09 |