오늘 할일
🌐 Spring MVC 웹 애플리케이션 흐름 정리
📌 1. RequestMapping & 파라미터 처리
✅ 요청 매핑
@RequestMapping("/process")
public String processForm(@ModelAttribute User user) {
// form 데이터가 자동으로 User 객체에 매핑됨
}
- @ModelAttribute: 폼에서 넘어온 여러 개의 파라미터를 객체로 받아 처리
- DTO 객체와 필드명이 같으면 자동으로 매핑됨
✅ 단일 파라미터 처리
@RequestMapping("/login")
public String login(@RequestParam String accountName,
@RequestParam String password) {
// 단일 파라미터를 직접 받아옴
}
📌 2. DB 연동 – MyBatis 사용 흐름
✅ MyBatis 구성 흐름
- 인터페이스 정의
public interface UserMapper {
User selectByAccount(String accountName);
}
2. XML 매핑 파일
<select id="selectByAccount" resultType="User">
SELECT * FROM users WHERE account_name = #{accountName}
</select>
- 쿼리 1개 = 메서드 1개 = XML 태그 1개
- SQL은 XML에, 메서드는 인터페이스에 정의
📌 3. 로그인 처리 흐름
- 로그인 요청 → @RequestParam으로 값 받기
- MyBatis로 DB에서 SELECT
- 로그인 성공 시 HttpSession에 사용자 정보 저장
- 이후 인증 체크는 세션 기반으로 처리
session.setAttribute("loginUser", user);
📌 4. Redirect의 의미
return "redirect:/home";
- HTML을 바로 응답하는 게 아님
- 브라우저에게 다시 요청하라고 지시함
- 즉, 2번 요청이 발생함 (POST-Redirect-GET 패턴)
📌 5. 게시판 & Thymeleaf 문법
✅ 로그인 여부에 따른 처리
<div th:if="${session.loginUser != null}">
<p th:text="${session.loginUser.name} + '님 환영합니다'"></p>
<a th:href="@{/logout}">로그아웃</a>
</div>
- th:if: 조건부 렌더링
- th:text: 텍스트 출력
- th:href: 링크 처리
📌 6. 수정(Form) 화면에서 hidden 태그가 필요한 이유
<input type="hidden" name="id" th:value="${post.id}" />
- 기본키(ID) 값을 숨겨서 전송하기 위함
- 사용자는 수정 시 id 값을 볼 수 없지만, 서버는 어떤 게시물을 수정할지 알아야 함
- 수정 처리는 보통 POST로 이루어지므로 id를 form에 포함해야 함
📌 7. List vs Map – 반복문 처리
- List: 반복문(th:each) 돌릴 때 사용
- Map: 특정 key로 객체를 꺼낼 때 사용
(DTO와 비슷한 방식으로 속성 접근 가능)
<!-- List 예시 -->
<tr th:each="item : ${postList}">
<td th:text="${item.title}"></td>
</tr>
<!-- Map 예시 -->
<p th:text="${userMap['admin'].name}"></p>
댓글 기능 구현
1.먼저 db에다 fp_comment 테이블 만들기
Create Table fp_comment(
id int primary key auto_increment,
articleId int,
userId int,
message VARCHAR(3000),
created_at DATETIME default NOW(),
FOREIGN KEY (articleId) REFERENCES fp_article(id) ON DELETE CASCADE,
FOREIGN KEY (userId) REFERENCES fp_user(id) ON DELETE CASCADE
);
- foreign key 사용한 이유 :
- 외래키를 댓글(CommentDto)을 등록할 때 이 댓글이 어떤 글(articleId)에 속하고, 누가(userId) 썼는지를 알아야 함
- 두 테이블을 연결하는 다리역할(다른테이블의 기본키 참조)
- 데이터 무결성을 위해
- on delete cascade : foreign key로 테이블 간 관계를 연결해놨을 때, 부모 테이블의 행이 삭제되면 자식 테이블의 연관된 행도 자동으로 삭제되게 해주는 옵션
처음엔 외래키 설정 안 했을 시 → articleId넘기려고할때 자꾸 db든 서버든 articleId가 null값이 들어감
1. articleId가 null로 저장되는 문제
- 폼이나 DTO에서는 값이 들어왔는데, DB에는 null로 들어가는 경우 많음
- 왜? 외래키 없으면 DB가 “이 값 진짜 존재하는 article인지” 검사 안 함
- 실수로 빠지거나, 컨트롤러에서 안 넣어줘도 DB는 에러 없이 그냥 null 저장
2. 존재하지 않는 게시글 ID로 댓글 저장 가능
- 예: articleId = 999 이렇게 저장해도 DB는 에러 안 냄
- 실제로는 그런 게시글이 없는데, 댓글만 DB에 저장됨 → 고아 데이터 생김
3. JOIN 쿼리에서 에러 나거나 결과 없음
- 댓글은 있는데 articleId가 null이거나, 존재하지 않는 게시글 ID라면?
- 조인할 때 결과 안 뜨거나 null 뜸 → 프론트에서 에러 나기 딱 좋음
CommentDto 생성
package com.ca.finalproject.dto;
import java.time.LocalDateTime;
import lombok.Data;
@Data
public class CommentDto {
private int id; // 댓글 고유 번호 (PK)
private int articleId; // 어떤 게시글에 달린 댓글인지
private int userId; // 댓글 작성자
private String message; // 댓글 내용
private LocalDateTime createdAt; // 작성 시각
}
readArticlePage.html에다 form 추가
<div class="row mb-4">
<div class="col-md-12">
<form action="/board/writeComment" method="post">
<input type="hidden" name="articleId" th:value="${articleData.articleDto.id}" />
<div class="input-group">
<span class="input-group-text">
<i class="bi bi-chat-dots"></i>
</span>
<input type="text" name="message" class="form-control" placeholder="댓글을 입력하세요" required>
<button type="submit" class="btn btn-primary">작성</button>
</div>
</form>
</div>
</div>
- 사용자는 그냥 댓글만 입력해 (<input type="text" name="message" ...>).
- form이 submit될 때 articleId도 함께 서버로 전송됨 (숨겨진 상태로).
- 서버는 이 articleId를 보고 “이 댓글은 몇 번 게시글에 달린 거구나” 판단.
- 댓글 DB에 저장할 때 그걸 같이 넣음.
BoardController.java
@RequestMapping("writeComment")
public String writeComment(CommentDto commentDto,HttpSession session){
UserDto sessionUser = (UserDto) session.getAttribute("sessionUserInfo");
// System.out.println("sessionUser: " + sessionUser);
if (sessionUser == null) {
System.out.println("로그인 세션이 없습니다.");
return "redirect:/user/loginPage"; // 로그인 안된 경우 로그인 페이지로 보냄
}
commentDto.setUserId(sessionUser.getId());
boardService.writeComment(commentDto);
return "redirect:/board/readArticlePage?id=" + commentDto.getArticleId();
}
BoardServiceImpl.java
public void writeComment(CommentDto commentDto){
commentSqlMapper.insertComment(commentDto);
}
public List<Map<String, Object>> getCommentListByArticleId(int articleId){
List<Map<String, Object>> resultList = new ArrayList<>();
List<CommentDto> commentList = commentSqlMapper.findCommentsByArticleId(articleId);
for(CommentDto commentDto : commentList){
Map<String, Object> map = new HashMap<>();
map.put("commentDto", commentDto);
UserDto userDto = userSqlMapper.findUserById(commentDto.getUserId());
map.put("userDto", userDto);
resultList.add(map);
}
return resultList;
}
CommentSqlMapper.java
package com.ca.finalproject.board.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.ca.finalproject.dto.CommentDto;
@Mapper
public interface CommentSqlMapper {
public void insertComment(CommentDto commentDto);
public List<CommentDto> findCommentsByArticleId(int articleId);
public int countCommentsByArticleId(int articleId);
}
CommentSqlMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ca.finalproject.board.mapper.CommentSqlMapper">
<select id="findCommentsByArticleId" >
SELECT * FROM fp_comment WHERE articleId = #{articleId} ORDER BY created_at ASC
</select>
<insert id="insertComment">
INSERT INTO fp_comment (articleId,userId,message)
VALUES (#{articleId},#{userId},#{message});
</insert>
<select id="countCommentsByArticleId">
select count(*) from fp_comment where articleId=#{articleId}
</select>
</mapper>
근데 여기서 문제점 발견... db에단 commentCount가 연결안됨..

하지만 화면 출력,콘솔 출력은 잘됨


뭐가 문제일까..
다시 해보쟈
'중앙정보기술인재개발원 > Spring' 카테고리의 다른 글
| [Spring] 카카오페이 단건결제 API (2) | 2025.06.21 |
|---|---|
| [Spring] final project_3차 (0) | 2025.05.12 |
| [Spring] final project_2차 (0) | 2025.05.12 |
| [Spring] | 세션,쿠키,Session,Cookie (0) | 2025.05.02 |
| [Spring] final project_4 (0) | 2025.05.02 |