커밋 메시지

This commit is contained in:
hyunu00
2024-09-20 09:19:19 +09:00
parent 6bc0afd938
commit 1bd3e33c97
19 changed files with 686 additions and 192 deletions

View File

@ -38,7 +38,6 @@ header {
display: flex;
flex-direction: column;
gap: 20px;
background-color: #f1f1f1;
padding: 20px;
height: calc(100vh - 60px);
}
@ -48,10 +47,14 @@ header {
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: #ccc;
}
.login-section {
background-color: #ccc;
}
.login-button {
padding: 10px;
width: 100%;
@ -82,6 +85,7 @@ hr{
/*메인*/
/* 테이블 스타일 */
table {
width: 100%;
border-collapse: collapse;
@ -89,16 +93,17 @@ table {
}
table th, table td {
border: 1px solid #ccc;
border: none; /* 모든 테두리 제거 */
padding: 10px;
text-align: left;
border-right:none;
border-left:none;
border-top:none;
border-bottom:none;
}
/* 세로줄, 가로줄 모두 제거 */
table td, table th {
border: none; /* 완전히 모든 테두리 제거 */
}
.write-button-container {
display: flex;
justify-content: center;

View File

@ -1,17 +1,21 @@
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Routes, Link, useNavigate } from 'react-router-dom';
import { Route, Routes, Link, useNavigate } from 'react-router-dom';
import Register from './register';
import Login from './login';
import MyPosts from './MyPosts';
import EditProfile from './EditProfile';
import BoardList from './BoardList';
import CreatePost from './CreatePost';
import FindId from './FindId';
import FindPassword from './FindPassword';
import './App.css';
function App() {
const [user, setUser] = useState(null); // 로그인된 사용자 정보를 저장하는 상태
const [postCount, setPostCount] = useState(0);
const [error, setError] = useState(null); // API 호출 중 발생한 에러 상태
const [error, setError] = useState(null);
const navigate = useNavigate();
// 로그인 상태 유지
useEffect(() => {
@ -19,12 +23,14 @@ function App() {
try {
const response = await fetch('http://localhost:8080/users/current-user', {
method: 'GET',
credentials: 'include', // 세션 기반 쿠키 전송
credentials: 'include',
});
if (response.ok) {
const user = await response.json();
setUser(user);
} else if (response.status === 401) {
setUser(null); // 인증 실패 시 로그아웃 상태로 설정
}
} catch (error) {
console.error('현재 사용자 정보 불러오기 실패:', error);
@ -55,61 +61,67 @@ function App() {
}, [user]);
return (
<Router>
<div className="container">
{/* 고정된 헤더 */}
<header>
<div className="home-link"> &gt;</div>
<div className="banner">메인 배너</div>
</header>
<div className="container">
{/* 고정된 헤더 */}
<header>
<div className="home-link"> &gt;</div>
<div className="banner">메인 배너</div>
</header>
{/* 고정된 사이드바 */}
<aside className="aside-container">
<RenderAside user={user} setUser={setUser} postCount={postCount} error={error} />
<div className="aside-section">
<ul className="category">
<li>카테고리</li>
<hr width="100%" color="black"></hr>
<Link to="/">전체게시판</Link><br />
<Link to="/shared">게시판</Link><br />
<Link to="/questions">질문게시판</Link><br />
<Link to="/free">자유게시판</Link>
</ul>
</div>
</aside>
{/* 고정된 사이드바 */}
<aside className="aside-container">
<RenderAside user={user} setUser={setUser} postCount={postCount} error={error} />
<div className="aside-section">
<ul className="category">
<li>카테고리</li>
<hr width="100%" color="black"></hr>
<Link to="/">전체게시판</Link><br />
<Link to="/shared">게시판</Link><br />
<Link to="/questions">질문게시판</Link><br />
<Link to="/free">자유게시판</Link>
</ul>
</div>
</aside>
{/* 동적 콘텐츠 영역 */}
<main>
<Routes>
<Route path="/" element={<BoardList />} />
<Route path="/register" element={<Register />} />
<Route path="/login" element={<Login setUser={setUser} />} />
<Route path="/my-posts" element={<MyPosts user={user} />} />
<Route path="/edit-profile" element={<EditProfile user={user} />} />
<Route path="/create-post" element={<CreatePost user={user} />} /> {/* 글쓰기 페이지 */}
</Routes>
</main>
{/* 동적 콘텐츠 영역 */}
<main>
<Routes>
<Route path="/" element={<BoardList />} />
<Route path="/register" element={<Register />} />
<Route path="/login" element={<Login setUser={setUser} />} />
<Route path="/find-id" element={<FindId />} />
<Route path="/find-password" element={<FindPassword />} />
{/* 로그인 후에만 접근 가능한 페이지 */}
{user && (
<>
<Route path="/my-posts" element={<MyPosts user={user} />} />
<Route path="/edit-profile" element={<EditProfile user={user} />} />
<Route path="/create-post" element={<CreatePost user={user} />} />
</>
)}
</Routes>
</main>
{/* 글쓰기 링크 */}
<Link to="/create-post">글쓰기</Link>
</div>
</Router>
{/* 글쓰기 링크 - 로그인한 사용자만 글쓰기가 가능함 */}
{user && <Link to="/create-post">글쓰기</Link>}
</div>
);
}
function RenderAside({ user, setUser, postCount, error }) {
const navigate = useNavigate(); // useNavigate는 Router 내부에서만 사용 가능
const navigate = useNavigate(); // useNavigate 훅 사용
// 로그아웃 처리
const handleLogout = async () => {
try {
const response = await fetch('http://localhost:8080/users/logout', {
method: 'POST',
credentials: 'include', // 세션 무효화 요청
credentials: 'include', // 세션 무효화 요청
});
if (response.ok) {
localStorage.removeItem('token'); // 로그아웃 시 세션 쿠키를 이용하므로 토큰 대신 상태 초기화
localStorage.removeItem('token');
setUser(null);
alert('로그아웃 되었습니다.');
navigate('/login'); // 로그아웃 후 로그인 페이지로 이동
@ -125,24 +137,34 @@ function RenderAside({ user, setUser, postCount, error }) {
return <div>오류 발생: {error}</div>;
}
if (!user) {
return (
<ul className="login-menu">
<Link to="/login">로그인</Link><br />
<Link to="/register">회원가입</Link><br />
<Link to="/find-id">아이디/비밀번호 찾기</Link><br />
</ul>
);
}
return (
<div className="aside-section">
<p>닉네임: {user.userNickname}</p>
<p>아이디: {user.userId}</p>
<p>내가 개수: {postCount}</p>
<Link to="/my-posts">내가 </Link><br />
<Link to="/edit-profile">개인정보 수정</Link><br />
<button onClick={handleLogout}>로그아웃</button>
{!user ? (
<div className="login-section">
<ul className="login-menu">
<Link to="/login">로그인</Link><br />
<Link to="/register">회원가입</Link><br />
<Link to="/find-id">아이디 찾기</Link><br />
<Link to="/find-password">비밀번호 찾기</Link><br />
</ul>
</div>
) : (
<div className="user-info">
{user.userImage && (
<img
src={`data:image/jpeg;base64,${user.userImage}`}
alt="프로필 이미지"
style={{ width: '100px', height: '100px', borderRadius: '50%' }}
/>
)}
<p>닉네임: {user.userNickname}</p>
<p>아이디: {user.userId}</p>
<p>내가 개수: {postCount}</p>
<Link to="/my-posts">내가 </Link><br />
<Link to="/edit-profile">개인정보 수정</Link><br />
<button onClick={handleLogout}>로그아웃</button>
</div>
)}
</div>
);
}

View File

@ -1,23 +1,25 @@
import React, { useEffect, useState } from 'react';
import './App.css'; // CSS 파일 추가
function BoardList() {
const [boards, setBoards] = useState([]);
const [error, setError] = useState(null); // 에러 상태 추가
const [error, setError] = useState(null);
useEffect(() => {
const fetchBoards = async () => {
try {
const response = await fetch('http://localhost:8080/boards');
if (!response.ok) {
// 에러가 발생한 경우 JSON 응답에서 메시지만 추출
const errorData = await response.json();
setError(errorData.message || '서버에서 오류가 발생했습니다.');
} else {
const data = await response.json();
setBoards(data);
// 최신 글이 위로 오도록 정렬 (내림차순)
const sortedData = data.sort((a, b) => new Date(b.updatedDate) - new Date(a.updatedDate));
setBoards(sortedData);
}
} catch (err) {
// 네트워크 오류가 발생했을 때 처리
setError('네트워크 오류가 발생했습니다.');
}
};
@ -26,18 +28,34 @@ function BoardList() {
}, []);
if (error) {
// 에러가 있을 때 에러 메시지를 출력
return <div>오류: {error}</div>;
}
return (
<div>
<h2>게시판 목록</h2>
<ul>
{boards.map((board) => (
<li key={board.boardNumber}>{board.boardTitle}</li>
))}
</ul>
<table className="board-table">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>내용</th>
<th>업데이트 날짜</th>
<th>작성자</th>
</tr>
</thead>
<tbody>
{boards.map((board) => (
<tr key={board.boardNumber}>
<td>{board.boardNumber}</td>
<td>{board.boardTitle}</td>
<td>{board.boardWrite.length > 10 ? board.boardWrite.substring(0, 10) + '...' : board.boardWrite}</td>
<td>{new Date(board.updatedDate).toLocaleString()}</td>
<td>{board.user.userNickname}</td> {/* userNickname 필드가 있는지 확인 필요 */}
</tr>
))}
</tbody>
</table>
</div>
);
}

View File

@ -1,9 +1,11 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
function CreatePost() {
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [category, setCategory] = useState(1); // 카테고리 기본값 설정
const [category, setCategory] = useState(1);
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
@ -11,7 +13,7 @@ function CreatePost() {
const newPost = {
boardTitle: title,
boardWrite: content,
boardCategory: category // 선택된 카테고리 값
boardCategory: category
};
try {
@ -20,16 +22,25 @@ function CreatePost() {
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify(newPost),
});
if (response.ok) {
alert('글 작성 완료');
setTitle('');
setContent('');
if (!response.ok) {
console.error(`Error: ${response.status}`); // 상태 코드를 출력하여 문제 확인
if (response.status === 401) {
alert('로그인이 필요합니다.');
navigate('/login'); // 로그인 페이지로 이동
} else if (response.status === 400) {
alert('잘못된 요청입니다.');
} else {
alert('글 작성 실패');
}
} else {
alert('글 작성 실패');
alert('글 작성이 완료되었습니다.');
navigate('/'); // 메인 페이지로 이동
}
} catch (error) {
console.error('글 작성 오류:', error);
}
@ -57,7 +68,7 @@ function CreatePost() {
<label>카테고리:</label>
<select value={category} onChange={(e) => setCategory(Number(e.target.value))}>
<option value={1}>자유게시판</option>
<option value={2}>게시판</option>
<option value={2}>게시판</option>
<option value={3}>질문게시판</option>
</select>
<br />

View File

@ -1,57 +1,102 @@
import React, { useState } from 'react';
import React, { useState } from 'react';
function EditProfile({ user }) {
const [nickname, setNickname] = useState(user ? user.userNickname : '');
const [password, setPassword] = useState('');
function EditProfile({ user }) {
const [nickname, setNickname] = useState(user ? user.userNickname : '');
const [name, setName] = useState(user ? user.userName : '');
const [userId] = useState(user ? user.userId : ''); // ID는 수정하지 않음
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [profileImage, setProfileImage] = useState(null);
const handleUpdate = async (e) => {
e.preventDefault();
const handleUpdate = async (e) => {
e.preventDefault();
const updatedData = { userNickname: nickname, userPassword: password };
try {
const response = await fetch(`http://localhost:8080/users/${user.userId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(updatedData),
});
if (response.ok) {
alert('프로필이 업데이트되었습니다.');
} else {
alert('업데이트 실패');
if (password !== confirmPassword) {
alert('비밀번호가 일치하지 않습니다.');
return;
}
} catch (error) {
console.error('업데이트 에러:', error);
}
};
return (
<div>
<h2>프로필 수정</h2>
<form onSubmit={handleUpdate}>
<label>닉네임:</label>
<input
type="text"
value={nickname}
onChange={(e) => setNickname(e.target.value)}
required
/><br />
const formData = new FormData();
formData.append('userNickname', nickname);
formData.append('userName', name);
formData.append('userPassword', password);
formData.append('profileImage', profileImage); // 이미지 파일 추가
<label>비밀번호:</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/><br />
try {
const response = await fetch(`http://localhost:8080/users/${userId}`, {
method: 'PUT',
body: formData,
credentials: 'include',
});
<button type="submit">수정하기</button>
</form>
</div>
);
}
if (response.ok) {
alert('프로필이 업데이트되었습니다.');
} else {
alert('업데이트 실패');
}
} catch (error) {
console.error('업데이트 에러:', error);
}
};
export default EditProfile;
const handleImageChange = (e) => {
setProfileImage(e.target.files[0]);
};
return (
<div>
<h2>프로필 수정</h2>
<form onSubmit={handleUpdate}>
<label>프로필 사진:</label>
<input
type="file"
accept="image/*"
onChange={handleImageChange}
/><br />
<label>닉네임:</label>
<input
type="text"
value={nickname}
onChange={(e) => setNickname(e.target.value)}
required
/><br />
<label>이름:</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
required
/><br />
<label>아이디:</label>
<input
type="text"
value={userId}
disabled
/><br />
<label>비밀번호:</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/><br />
<label>비밀번호 확인:</label>
<input
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
/><br />
<button type="submit">수정하기</button>
</form>
</div>
);
}
export default EditProfile;

42
frontend/src/FindId.js Normal file
View File

@ -0,0 +1,42 @@
import React, { useState } from 'react';
function FindId() {
const [nickname, setNickname] = useState('');
const [message, setMessage] = useState('');
const handleFindId = async (e) => {
e.preventDefault();
try {
const response = await fetch('http://localhost:8080/users/find-id', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ nickname }),
});
const result = await response.text();
setMessage(result);
} catch (error) {
setMessage('아이디 찾기 중 오류 발생.');
}
};
return (
<div>
<h2>아이디 찾기</h2>
<form onSubmit={handleFindId}>
<label>닉네임:</label>
<input
type="text"
value={nickname}
onChange={(e) => setNickname(e.target.value)}
required
/>
<button type="submit">아이디 찾기</button>
</form>
{message && <p>{message}</p>}
</div>
);
}
export default FindId;

View File

@ -0,0 +1,50 @@
import React, { useState } from 'react';
function FindPassword() {
const [nickname, setNickname] = useState('');
const [userId, setUserId] = useState('');
const [message, setMessage] = useState('');
const handleFindPassword = async (e) => {
e.preventDefault();
try {
const response = await fetch('http://localhost:8080/users/find-password', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ nickname, userId }),
});
const result = await response.text();
setMessage(result);
} catch (error) {
setMessage('비밀번호 찾기 중 오류 발생.');
}
};
return (
<div>
<h2>비밀번호 찾기</h2>
<form onSubmit={handleFindPassword}>
<label>닉네임:</label>
<input
type="text"
value={nickname}
onChange={(e) => setNickname(e.target.value)}
required
/>
<label>아이디:</label>
<input
type="text"
value={userId}
onChange={(e) => setUserId(e.target.value)}
required
/>
<button type="submit">비밀번호 찾기</button>
</form>
{message && <p>{message}</p>}
</div>
);
}
export default FindPassword;

View File

@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react';
import './App.js'; // 테이블 스타일 적용
function MyPosts({ user }) {
const [posts, setPosts] = useState([]);
@ -18,11 +19,29 @@ function MyPosts({ user }) {
<div>
<h2>{user?.userNickname}님의 게시물</h2>
{posts.length > 0 ? (
<ul>
{posts.map((post) => (
<li key={post.boardNumber}>{post.boardTitle}</li>
))}
</ul>
<table className="board-table">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>내용</th>
<th>업데이트 날짜</th>
<th>작성자</th>
</tr>
</thead>
<tbody>
{posts.map((post) => (
<tr key={post.boardNumber}>
<td>{post.boardNumber}</td>
<td>{post.boardTitle}</td>
<td>{post.boardWrite.length > 10 ? post.boardWrite.substring(0, 10) + '...' : post.boardWrite}</td>
{/* 날짜와 시간 모두 표시 */}
<td>{new Date(post.updatedDate).toLocaleString()}</td>
<td>{user.userNickname}</td> {/* 작성자는 항상 현재 사용자 */}
</tr>
))}
</tbody>
</table>
) : (
<p>게시물이 없습니다.</p>
)}

View File

@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter as Router } from 'react-router-dom'; // React Router 추가
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
@ -7,7 +8,9 @@ import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
<Router> {/* App 컴포넌트를 Router로 감싸기 */}
<App />
</Router>
</React.StrictMode>
);

View File

@ -16,7 +16,7 @@ function Login({ setUser }) {
return;
}
const credentials = { userId, userPassword: password }; // 서버에 맞춘 데이터 이름
const credentials = { userId, userPassword: password };
setLoading(true);
setErrorMessage(''); // 에러 메시지 초기화
@ -27,19 +27,19 @@ function Login({ setUser }) {
'Content-Type': 'application/json',
},
body: JSON.stringify(credentials),
credentials: 'include', // 세션 쿠키 전송
credentials: 'include',
});
if (response.ok) {
// 로그인 성공 시 사용자 정보 요청
const userResponse = await fetch('http://localhost:8080/users/current-user', {
method: 'GET',
credentials: 'include', // 세션 기반 요청
credentials: 'include',
});
if (userResponse.ok) {
const user = await userResponse.json();
setUser(user); // 로그인된 사용자 정보를 상태에 저장
setUser(user);
alert('로그인 성공');
navigate('/');
} else {

View File

@ -10,7 +10,7 @@ public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000") // React 애플리케이션의 주소
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true);

View File

@ -16,12 +16,12 @@ import com.example.demo.service.UserService;
public class BoardController {
private final BoardService boardService;
private final UserService userService; // UserService를 추가하여 사용자를 조회
private final UserService userService;
@Autowired
public BoardController(BoardService boardService, UserService userService) {
this.boardService = boardService;
this.userService = userService; // UserService 주입
this.userService = userService;
}
// 전체 게시글 가져오기
@ -42,6 +42,7 @@ public class BoardController {
return boardService.getPostsByUserId(userId);
}
// 게시글 생성 (세션에서 로그인된 사용자 정보 사용)`
@PostMapping("/create")
public ResponseEntity<?> createPost(@RequestBody Board board, HttpSession session) {
try {
@ -55,11 +56,31 @@ public class BoardController {
board.setUser(loggedInUser);
// 게시글 저장
Board createdBoard = boardService.saveBoard(board);
Board createdBoard = boardService.saveBoard(board, loggedInUser.getUserId()); // 두 번째 인자로 loggedInUser.getUserId() 전달
return ResponseEntity.ok("게시글 작성 완료");
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("게시글 작성 중 오류 발생: " + e.getMessage());
}
}
// Board 생성 시 로그인된 사용자의 ID를 사용하여 CRT_USER, UDT_USER 설정
@PostMapping
public ResponseEntity<Board> createBoard(@RequestBody Board board, HttpSession session) {
String loggedInUser = (String) session.getAttribute("userId");
board.setUpdatedBy(loggedInUser);
Board createdBoard = boardService.saveBoard(board, loggedInUser);
return new ResponseEntity<>(createdBoard, HttpStatus.CREATED);
}
// Board 수정 시 UDT_USER 업데이트
@PutMapping("/{id}")
public ResponseEntity<Board> updateBoard(@PathVariable Long id, @RequestBody Board boardDetails, HttpSession session) {
String loggedInUser = (String) session.getAttribute("userId");
boardDetails.setUpdatedBy(loggedInUser);
Board updatedBoard = boardService.saveBoard(boardDetails, loggedInUser);
return ResponseEntity.ok(updatedBoard);
}
}

View File

@ -1,5 +1,6 @@
package com.example.demo.controller;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@ -17,23 +18,38 @@ public class UserController {
this.userService = userService;
}
// 전체 사용자 목록 가져오기
@GetMapping
public List<User> getUsers() {
return userService.getAllUsers();
}
// POST 요청을 처리하는 메서드 (회원가입)
// 사용자 생성 (회원가입)
@PostMapping("/register")
public ResponseEntity<?> createUser(@RequestBody User user) {
public ResponseEntity<?> createUser(@RequestBody User user, HttpSession session) {
try {
// 로그인된 사용자 ID를 세션에서 가져오기
User loggedInUser = (User) session.getAttribute("user");
if (loggedInUser == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("로그인이 필요합니다.");
}
// 사용자 생성 시 필수 필드 확인 (유효성 검사)
if (user.getUserId() == null || user.getUserPassword() == null || user.getUserName() == null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("필수 필드가 누락되었습니다.");
}
// 생성자 정보 설정
user.setCreatedBy(loggedInUser.getUserId());
user.setUpdatedBy(loggedInUser.getUserId());
User createdUser = userService.saveUser(user);
return ResponseEntity.ok("회원가입 성공");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("사용자 생성 중 오류 발생: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("회원가입 실패: " + e.getMessage());
}
}
// 로그인 요청 처리
// 로그인 처리
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody User user, HttpSession session) {
User authenticatedUser = userService.authenticate(user.getUserId(), user.getUserPassword());
@ -46,7 +62,6 @@ public class UserController {
return ResponseEntity.ok("로그인 성공");
}
// 로그아웃 처리
@PostMapping("/logout")
public ResponseEntity<?> logout(HttpSession session) {
@ -64,15 +79,95 @@ public class UserController {
return ResponseEntity.ok(user);
}
// Update an existing user (UPDATE)
// 사용자 정보 업데이트 (닉네임, 이름 등)
@PutMapping("/{id}")
public User updateUser(@PathVariable String id, @RequestBody User userDetails) {
return userService.updateUser(id, userDetails);
public ResponseEntity<?> updateUserProfile(
@PathVariable String id,
@RequestPart("userNickname") String nickname,
@RequestPart("userName") String name,
@RequestPart("userPassword") String password,
@RequestPart(value = "profileImage", required = false) MultipartFile profileImage,
HttpSession session) {
try {
// 로그인된 사용자 확인
User loggedInUser = (User) session.getAttribute("user");
if (loggedInUser == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("로그인이 필요합니다.");
}
// 사용자 정보 업데이트 처리
// 이미지 파일 처리 로직 추가 필요
userService.updateUserProfile(id, nickname, name, password, profileImage);
return ResponseEntity.ok("프로필이 업데이트되었습니다.");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("프로필 업데이트 실패: " + e.getMessage());
}
}
// Delete a user (DELETE)
// 비밀번호 업데이트 (별도 API)
@PutMapping("/{id}/password")
public ResponseEntity<?> updatePassword(@PathVariable String id, @RequestBody String newPassword, HttpSession session) {
try {
// 로그인된 사용자 확인
User loggedInUser = (User) session.getAttribute("user");
if (loggedInUser == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("로그인이 필요합니다.");
}
// 비밀번호 업데이트
userService.updatePassword(id, newPassword);
return ResponseEntity.ok("비밀번호 업데이트 성공");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("비밀번호 업데이트 실패: " + e.getMessage());
}
}
// 사용자 삭제
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable String id) {
userService.deleteUser(id);
public ResponseEntity<?> deleteUser(@PathVariable String id, HttpSession session) {
try {
// 로그인된 사용자 확인
User loggedInUser = (User) session.getAttribute("user");
if (loggedInUser == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("로그인이 필요합니다.");
}
userService.deleteUser(id);
return ResponseEntity.ok("사용자 삭제 성공");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("사용자 삭제 실패: " + e.getMessage());
}
}
// 닉네임으로 아이디 찾기
@PostMapping("/find-id")
public ResponseEntity<?> findIdByNickname(@RequestParam String nickname) {
try {
String userId = userService.findUserIdByNickname(nickname);
if (userId != null) {
return ResponseEntity.ok("해당 닉네임의 아이디는 " + userId + "입니다.");
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("해당 닉네임으로 등록된 아이디가 없습니다.");
}
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("아이디 찾기 중 오류 발생: " + e.getMessage());
}
}
// 닉네임과 아이디로 비밀번호 찾기
@PostMapping("/find-password")
public ResponseEntity<?> findPasswordByNicknameAndId(@RequestParam String nickname, @RequestParam String userId) {
try {
String userPassword = userService.findUserPasswordByNicknameAndId(nickname, userId);
if (userPassword != null) {
return ResponseEntity.ok("해당 닉네임과 아이디의 비밀번호는 " + userPassword + "입니다.");
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("닉네임 또는 아이디가 일치하지 않습니다.");
}
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("비밀번호 찾기 중 오류 발생: " + e.getMessage());
}
}
}

View File

@ -19,21 +19,36 @@ public class Board {
@Column(name = "BOARD_TITLE", nullable = false)
private String boardTitle;
// VARCHAR2(4000)으로 설정된 필드
@Column(name = "BOARD_WRITE", nullable = false, length = 4000)
private String boardWrite;
@Column(name = "BOARD_DATE")
private LocalDateTime boardDate;
@ManyToOne
@JoinColumn(name = "USER_ID", nullable = false)
private User user;
// 자동으로 게시글 작성 시간을 현재 시간으로 설정
@Column(name = "CRT_DT", nullable = false)
private LocalDateTime createdDate;
@Column(name = "CRT_USER", nullable = false)
private String createdBy;
@Column(name = "UDT_DT", nullable = false)
private LocalDateTime updatedDate;
@Column(name = "UDT_USER", nullable = false)
private String updatedBy;
// @PrePersist: 엔티티가 처음 저장될 때 자동으로 날짜 필드를 설정
@PrePersist
protected void onCreate() {
this.boardDate = LocalDateTime.now();
this.createdDate = LocalDateTime.now();
this.updatedDate = LocalDateTime.now();
}
// @PreUpdate: 엔티티가 업데이트될 때 자동으로 날짜 필드를 변경
@PreUpdate
protected void onUpdate() {
this.updatedDate = LocalDateTime.now();
}
// Getters and Setters
@ -69,14 +84,6 @@ public class Board {
this.boardWrite = boardWrite;
}
public LocalDateTime getBoardDate() {
return boardDate;
}
public void setBoardDate(LocalDateTime boardDate) {
this.boardDate = boardDate;
}
public User getUser() {
return user;
}
@ -84,4 +91,36 @@ public class Board {
public void setUser(User user) {
this.user = user;
}
public LocalDateTime getCreatedDate() {
return createdDate;
}
public void setCreatedDate(LocalDateTime createdDate) {
this.createdDate = createdDate;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public LocalDateTime getUpdatedDate() {
return updatedDate;
}
public void setUpdatedDate(LocalDateTime updatedDate) {
this.updatedDate = updatedDate;
}
public String getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(String updatedBy) {
this.updatedBy = updatedBy;
}
}

View File

@ -1,21 +1,20 @@
package com.example.demo.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import java.sql.Blob;
import java.util.List;
import java.time.LocalDateTime;
@Entity
@Table(name = "USER_TB")
public class User {
@Id
@Column(name = "USER_ID")
@Column(name = "USER_ID", nullable = false)
private String userId;
@Column(name = "USER_NAME", nullable = false)
private String userName;
@Column(name = "USER_NICKNAME", unique = true, nullable = false)
@Column(name = "USER_NICKNAME", nullable = false, unique = true)
private String userNickname;
@Column(name = "USER_PW", nullable = false)
@ -23,16 +22,39 @@ public class User {
@Lob
@Column(name = "USER_IMAGE")
private Blob userImage;
private byte[] userImage;
@Column(name = "USER_LV")
private Integer userLevel = 1;
@Column(name = "USER_LV", nullable = false, columnDefinition = "NUMBER DEFAULT 1")
private Integer userLevel;
@JsonIgnore // 이 필드를 직렬화할 때 무시
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Board> boards;
@Column(name = "CRT_DT", nullable = false)
private LocalDateTime createdDate;
@Column(name = "CRT_USER", nullable = false)
private String createdBy;
@Column(name = "UDT_DT", nullable = false)
private LocalDateTime updatedDate;
@Column(name = "UDT_USER", nullable = false)
private String updatedBy;
// 레코드가 생성될 때 자동으로 설정
@PrePersist
protected void onCreate() {
this.createdDate = LocalDateTime.now();
this.createdBy = "System"; // 실제 사용자 정보로 변경 가능
}
// 레코드가 수정될 때 자동으로 설정
@PreUpdate
protected void onUpdate() {
this.updatedDate = LocalDateTime.now();
this.updatedBy = "System"; // 실제 사용자 정보로 변경 가능
}
// Getters and Setters
public String getUserId() {
return userId;
}
@ -65,11 +87,11 @@ public class User {
this.userPassword = userPassword;
}
public Blob getUserImage() {
public byte[] getUserImage() {
return userImage;
}
public void setUserImage(Blob userImage) {
public void setUserImage(byte[] userImage) {
this.userImage = userImage;
}
@ -81,11 +103,35 @@ public class User {
this.userLevel = userLevel;
}
public List<Board> getBoards() {
return boards;
public LocalDateTime getCreatedDate() {
return createdDate;
}
public void setBoards(List<Board> boards) {
this.boards = boards;
public void setCreatedDate(LocalDateTime createdDate) {
this.createdDate = createdDate;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public LocalDateTime getUpdatedDate() {
return updatedDate;
}
public void setUpdatedDate(LocalDateTime updatedDate) {
this.updatedDate = updatedDate;
}
public String getUpdatedBy() {
return updatedBy;
}
public void setUpdatedBy(String updatedBy) {
this.updatedBy = updatedBy;
}
}

View File

@ -5,4 +5,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.entity.User;
public interface UserRepository extends JpaRepository<User, String> {
User findByUserNickname(String nickname);
User findByUserNicknameAndUserId(String nickname, String userId);
}

View File

@ -1,9 +1,11 @@
package com.example.demo.service;
import com.example.demo.entity.User;
import org.springframework.stereotype.Service;
import com.example.demo.entity.Board;
import com.example.demo.repository.BoardRepository;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@Service
@ -28,7 +30,22 @@ public class BoardService {
}
@Transactional
public Board saveBoard(Board board) {
public Board saveBoard(Board board, String loggedInUser) {
if (board.getCreatedDate() == null) {
board.setCreatedBy(loggedInUser); // 새로 생성될 경우 생성자 정보 설정
}
board.setUpdatedBy(loggedInUser); // 수정자 정보 설정
return boardRepository.save(board);
}
// createBoard 메서드 수정
public void createBoard(Board board, User user) {
board.setUser(user); // ManyToOne 관계의 user 설정
board.setCreatedDate(LocalDateTime.now()); // 생성 시간 설정
board.setUpdatedDate(LocalDateTime.now()); // 업데이트 시간 설정
board.setCreatedBy(user.getUserId()); // 생성자 설정
board.setUpdatedBy(user.getUserId()); // 수정자 설정
boardRepository.save(board); // 데이터베이스에 저장
}
}

View File

@ -1,10 +1,12 @@
package com.example.demo.service;
import org.springframework.stereotype.Service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import java.util.List;
import java.util.Optional;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@Service
public class UserService {
@ -43,7 +45,33 @@ public class UserService {
return null; // 인증 실패 시 null 반환
}
// Update an existing user
// 사용자 프로필 업데이트 메서드
public void updateUserProfile(String id, String nickname, String name, String password, MultipartFile profileImage) {
Optional<User> userOptional = userRepository.findById(id);
if (userOptional.isPresent()) {
User user = userOptional.get();
user.setUserNickname(nickname);
user.setUserName(name);
user.setUserPassword(password);
// 프로필 이미지가 있을 경우 처리
if (profileImage != null && !profileImage.isEmpty()) {
try {
byte[] imageBytes = profileImage.getBytes();
user.setUserImage(imageBytes); // 이미지 저장 (Byte 배열로 저장하는 경우)
} catch (Exception e) {
throw new RuntimeException("프로필 이미지 처리 중 오류 발생", e);
}
}
// 변경 사항 저장
userRepository.save(user);
} else {
throw new RuntimeException("User not found with id " + id);
}
}
// 사용자 업데이트
@Transactional
public User updateUser(String id, User userDetails) {
Optional<User> userOptional = userRepository.findById(id);
if (userOptional.isPresent()) {
@ -51,13 +79,39 @@ public class UserService {
user.setUserName(userDetails.getUserName());
user.setUserNickname(userDetails.getUserNickname());
user.setUserPassword(userDetails.getUserPassword());
user.setUserImage(userDetails.getUserImage());
user.setUserLevel(userDetails.getUserLevel());
user.setUpdatedBy(userDetails.getUpdatedBy());
return userRepository.save(user);
} else {
throw new RuntimeException("User not found with id " + id);
}
}
// Delete a user
// 비밀번호 업데이트 메서드 추가
public void updatePassword(String id, String newPassword) {
Optional<User> userOptional = userRepository.findById(id);
if (userOptional.isPresent()) {
User user = userOptional.get();
user.setUserPassword(newPassword); // 새로운 비밀번호 설정
userRepository.save(user); // 변경 사항 저장
} else {
throw new RuntimeException("User not found with id " + id);
}
}
// 닉네임으로 사용자 아이디 찾기
public String findUserIdByNickname(String nickname) {
User user = userRepository.findByUserNickname(nickname);
return user != null ? user.getUserId() : null;
}
// 닉네임과 아이디로 비밀번호 찾기
public String findUserPasswordByNicknameAndId(String nickname, String userId) {
User user = userRepository.findByUserNicknameAndUserId(nickname, userId);
return user != null ? user.getUserPassword() : null;
}
public void deleteUser(String id) {
userRepository.deleteById(id);
}

View File

@ -7,6 +7,11 @@ spring.datasource.password=1004@
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.jpa.properties.hibernate.default_schema=HYUNWOO00
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
spring.mvc.static-path-pattern=/resources/**
spring.resources.static-locations=classpath:/demo/
spring.mvc.pathmatch.matching-strategy=ant-path-matcher