개인 프로젝트 개발 계기
최근 공부하다가 어떻게 놀까해서 스토쿠 책을 구매했다.
그런데 너무 어려워서 1시간이 걸리다보니 스도쿠 문제 풀어주는 웹 페이지가 있으면 좋겠다 싶어서 개발을 시작했다.
사용한 개발 툴
- Front : React
- Back : Spring Boot
추가로 적용할 것
AWS EC2로 배포
React.js와 Spring Boot를 하다보니 CORS 에러가 뜬다.
이에 대한 해결 방법으론 SecurityConfig, WebConfig 클래스를 만들어 해결한다.
# WebConfig.java
package com.example.demo;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000") // React 애플리케이션의 주소
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true);
}
}
# SecurityConfig.java
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable();
return http.build();
}
}
- 이 두 클래스를 적용하면 CORS가 해결된다!!!
# 행 확인하는 메소드
private boolean isValidRow(int[][] board, int row) {
Set<Integer> set = new HashSet<>();
for (int j = 0; j < 9; j++) {
int value = board[row][j];
if (value == 0) {
return false; // 하나라도 값이 입력되지 않았으면 false 반환
}
if (!set.add(value)) {
return false; // 중복된 값이 존재하면 false 반환
}
}
return true;
}
# 열 확인하는 코드
private boolean isValidColumn(int[][] board, int col) {
Set<Integer> set = new HashSet<>();
for (int i = 0; i < 9; i++) {
int value = board[i][col];
if (value == 0) {
return false; // 하나라도 값이 입력되지 않았으면 false 반환
}
if (!set.add(value)) {
return false; // 중복된 값이 존재하면 false 반환
}
}
return true;
}
# 3x3 작은 정사각형 확인하는 코드
private boolean isValidBox(int[][] board, int startRow, int startCol) {
Set<Integer> set = new HashSet<>();
for (int i = startRow; i < startRow + 3; i++) {
for (int j = startCol; j < startCol + 3; j++) {
int value = board[i][j];
if (value == 0) {
return false; // 하나라도 값이 입력되지 않았으면 false 반환
}
if (!set.add(value)) {
return false; // 중복된 값이 존재하면 false 반환
}
}
}
return true;
}
- 컨트롤러 한개만 만들어서 구현
# 스도쿠 초기 설정을 위한 배열 선언
const initialGrid: number[][] = Array.from({ length: 9 }, () => Array(9).fill(0));
# 스도쿠 해결하는 재귀함수
# 빈 칸에 1~9 수를 채워가며 가능한 수를 탐색하여 퍼즐 완성
const solveSudoku = (board: number[][]) => {
# 주어진 위치에 특정 숫자가 유효한지 확인하는 함수
const isValid = (row: number, col: number, num: number) => {
# 주어진 숫자가 같은 행이나 열에 이미 존재하는지 확인
for (let i = 0; i < 9; i++) {
if (board[row][i] === num || board[i][col] === num) {
return false;
}
}
# 3X3 작은 정사각형의 시작 위치를 계산
const startRow = Math.floor(row / 3) * 3;
const startCol = Math.floor(col / 3) * 3;
# 주어진 숫자가 작은 정사각형 안에 이미 존재하는지 확인
for (let i = startRow; i < startRow + 3; i++) {
for (let j = startCol; j < startCol + 3; j++) {
if (board[i][j] === num) {
return false;
}
}
}
return true;
};
# 스도쿠 해결하는 메인 함수
const solve = (row: number, col: number): boolean => {
# 모든 행 확인하면 해결
if (row === 9) {
return true;
}
# 이미 숫자가 채워져 있는 경우, 다음 위치로 이동하여 스도쿠 해결
if (board[row][col] !== 0) {
const nextRow = col === 8 ? row + 1 : row;
const nextCol = col === 8 ? 0 : col + 1;
return solve(nextRow, nextCol);
}
# 현재 위치에 숫자를 채워 넣는 부분
# 주어진 숫자가 유효하면 재귀적으로 다음 위치로 이동
for (let num = 1; num <= 9; num++) {
if (isValid(row, col, num)) {
board[row][col] = num;
const nextRow = col === 8 ? row + 1 : row;
const nextCol = col === 8 ? 0 : col + 1;
if (solve(nextRow, nextCol)) {
return true;
}
board[row][col] = 0;
}
}
return false;
};
# 마지막으로 solve 함수를 호출
solve(0, 0);
};
# 스도쿠 그리드가 유효한지 확인
const isValidSudoku = (grid: number[][]): boolean => {
# 숫자 배열이 유효한지 확인
const isValid = (arr: number[]): boolean => {
# 배열에서 0을 제외한 숫자들을 필터링하고 중복된 숫자 확인
const filtered = arr.filter(num => num !== 0);
return new Set(filtered).size === filtered.length;
};
# 행과 열이 유효한지 확인
for (let i = 0; i < 9; i++) {
if (!isValid(grid[i])) return false;
if (!isValid(grid.map(row => row[i]))) return false;
# 3X3 박스가 유효한지 확인
const startRow = Math.floor(i / 3) * 3;
const startCol = (i % 3) * 3;
const box = grid.slice(startRow, startRow + 3).flatMap(row => row.slice(startCol, startCol + 3));
if (!isValid(box)) return false;
}
return true;
};
# 최상위 컴포넌트
const App: React.FC = () => {
# 초기 스도쿠 그리드 상태값
const [grid, setGrid] = useState(initialGrid);
# 입력된 스도쿠 셀 값이 변경될 때 호출
const handleChange = (row: number, col: number, value: string) => {
const newGrid = grid.map((rowArr, r) =>
rowArr.map((cell, c) => (r === row && c === col ? Number(value) : cell))
);
setGrid(newGrid);
};
# 스도쿠 검증하기 위해 호출
const handleSubmit = () => {
axios.post('http://localhost:8080/api/validate', grid)
.then(response => {
if (response.data) {
alert('유효한 스토쿠입니다!');
} else {
alert('유효하지 않은 스토쿠입니다!');
}
})
.catch(error => {
console.error('Error validating sudoku:', error);
});
};
# 스도쿠 정답을 보여주기 위해 호출
const showSolution = () => {
const solvedGrid = [...grid];
solveSudoku(solvedGrid);
setGrid(solvedGrid);
};
return (
<div className="App">
<h1>스토쿠 풀이기</h1>
<div className="grid">
{grid.map((row, r) => (
<div key={r} className="row">
{row.map((cell, c) => (
<input
key={c}
type="number"
min="0"
max="9"
value={cell || ''}
onChange={(e) => handleChange(r, c, e.target.value)}
/>
))}
</div>
))}
</div>
<button onClick={handleSubmit}>검증하기</button>
<button onClick={showSolution}>정답보기</button>
</div>
);
};
포스팅을 마치며
1시간동안 스도쿠를 풀었는데,
이 것은 버튼 딸깍에 바로 풀어버린다.
1초만에 풀어버린다.
내 1시간!!!!
이제 EC2로 배포해보고 이번 개인 프로젝트는 마쳐야겠다.
'프로젝트 > 개인프로젝트' 카테고리의 다른 글
1일짜리 개인프로젝트 후기 (1) | 2024.05.09 |
---|---|
1일 개인프로젝트 기획 + 개발 바로 시작 (0) | 2024.05.08 |
개인프로젝트 기본 기획 (0) | 2024.04.23 |