Create + Read 만 (인가 없음 — 의도적으로)
회원·로그인·작성·수정·삭제·페이징·댓글·첨부파일·검색·관리자 권한·... 한꺼번에 만들면 어디서 막혀도 원인을 알 수 없습니다.
가장 단순한 두 기능부터 시작 — Create 와 Read.
가장 단순한 게시판 — 글 쓰기, 목록 보기, 상세 보기.
CREATE TABLE myboard (
num INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
writer VARCHAR(50) NOT NULL,
content TEXT NOT NULL
);
👉 4 컬럼만. 작성일·조회수·첨부 같은 「있으면 좋은」 컬럼은 일부러 없습니다 — 다음 차시에서 ALTER 로 한 칸씩.
package com.smhrd.domain;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@Data @AllArgsConstructor @NoArgsConstructor
public class Board {
private int num;
private String title;
private String writer;
private String content;
}
Lombok @Data 가 getter/setter 자동 생성. 폼의 name="title" ↔ 필드 title 이 일치해야 자동 바인딩.
package com.smhrd.mapper;
import com.smhrd.domain.Board;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface BoardMapper {
List<Board> selectList();
Board selectOne(int num);
void insert(Board b);
}
<mapper namespace="com.smhrd.mapper.BoardMapper">
<select id="selectList" resultType="com.smhrd.domain.Board">
SELECT num, title, writer, content
FROM myboard
ORDER BY num DESC
</select>
<select id="selectOne" parameterType="int"
resultType="com.smhrd.domain.Board">
SELECT num, title, writer, content
FROM myboard
WHERE num = #{num}
</select>
<insert id="insert" parameterType="com.smhrd.domain.Board"
useGeneratedKeys="true" keyProperty="num">
INSERT INTO myboard(title, writer, content)
VALUES(#{title}, #{writer}, #{content})
</insert>
</mapper>
useGeneratedKeys + keyProperty="num" — INSERT 후 DB 가 만든 자동 증가 num 값을 VO 의 num 필드에 채워 돌려줍니다.
v5 의 writer 컬럼은 로그인 ID 문자열 을 그대로 저장합니다. 닉네임 표시·JOIN 같은 「화려한 것」 은 일부러 미룸.
myboard
┌─────┬──────────┬─────────┬─────────┐
│ num │ title │ writer │ content │
├─────┼──────────┼─────────┼─────────┤
│ 1 │ 첫 글 │ user1 │ ... │
│ 2 │ 두번째 │ user2 │ ... │
└─────┴──────────┴─────────┴─────────┘
화면에는 ${b.writer} 그대로 출력
v5 는 「작동하는 최소형」이 목표. JOIN·표시명 분리 같은 기교는 후속 진화의 몫.
<insert id="insert" parameterType="com.smhrd.domain.Board"
useGeneratedKeys="true" keyProperty="num">
INSERT INTO myboard(title, writer, content)
VALUES(#{title}, #{writer}, #{content})
</insert>
DB 의 AUTO_INCREMENT 로 생성된 num 이 — INSERT 후 객체의 num 필드에 자동으로 채워집니다.
Board b = new Board();
b.setTitle("새 글");
b.setWriter("user1");
b.setContent("...");
mapper.insert(b);
System.out.println(b.getNum()); // ⭐ 자동 채워진 num (예: 7)
@Service
public class BoardService {
@Autowired
private BoardMapper mapper;
public List<Board> selectList() {
return mapper.selectList();
}
public Board selectOne(int num) {
Board b = mapper.selectOne(num);
if (b == null) {
throw new IllegalArgumentException("글이 없습니다: " + num);
}
return b;
}
public void insert(Board b) {
mapper.insert(b);
}
}
@Controller
@RequestMapping("/board")
public class BoardController {
@Autowired
private BoardService service;
@GetMapping("/list")
public String list(Model model) {
model.addAttribute("boards", service.selectList());
return "board/list";
}
@GetMapping("/view")
public String view(@RequestParam int num, Model model) {
model.addAttribute("board", service.selectOne(num));
return "board/view";
}
// ... (다음 슬라이드: 작성 폼 + 등록)
}
@GetMapping("/write")
public String writeForm() {
return "board/write";
}
@PostMapping("/write")
public String write(Board board, HttpSession session) {
Member u = (Member) session.getAttribute("loginUser");
board.setWriter(u.getId());
service.insert(board);
return "redirect:/board/list";
}
작성자 ID 는 폼이 아니라 세션에서 꺼냅니다. 폼으로 받으면 「남의 이름으로 쓰기」가 가능해지기 때문.
<!-- /WEB-INF/views/board/list.jsp -->
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<%@ include file="/WEB-INF/views/common/header.jsp" %>
<h1>게시판</h1>
<a href="/board/write">새 글 쓰기</a>
<table>
<thead>
<tr><th>번호</th><th>제목</th>
<th>작성자</th></tr>
</thead>
<tbody>
<c:forEach var="b" items="${boards}">
<tr>
<td>${b.num}</td>
<td><a href="/board/view?num=${b.num}">${b.title}</a></td>
<td>${b.writer}</td>
</tr>
</c:forEach>
</tbody>
</table>
</body></html>
「조회수」 컬럼이 없는 이유 — v5 의 myboard 에는 그 컬럼 자체가 없습니다. v10 에서 ALTER ADD.
c:forEach 가 게시판 목록 같은 반복 출력의 핵심.
<c:forEach var="b" items="${boards}">
<!-- ${b} 가 List 의 각 Board 객체 -->
${b.title}
</c:forEach>
Controller 에서:
model.addAttribute("boards", service.selectList()); // List<Board>
→ JSP 에서 ${boards} 가 그 List 를 가리킴. c:forEach 가 한 번씩 돌며 b 변수에 각 항목.
<!-- /WEB-INF/views/board/view.jsp -->
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<%@ include file="/WEB-INF/views/common/header.jsp" %>
<h1>${board.title}</h1>
<p>작성자: ${board.writer}</p>
<hr/>
<div>${board.content}</div>
<a href="/board/list">목록으로</a>
<!-- v6 에서 추가될 자리:
본인일 때만 수정·삭제 버튼 -->
</body></html>
<!-- /WEB-INF/views/board/write.jsp -->
<%@ page contentType="text/html; charset=UTF-8" %>
<html>
<body>
<%@ include file="/WEB-INF/views/common/header.jsp" %>
<h1>새 글 쓰기</h1>
<form action="/board/write" method="post">
<label>제목: <input name="title" required /></label><br/>
<label>내용:<br/>
<textarea name="content" rows="10" cols="50"></textarea>
</label><br/>
<button type="submit">저장</button>
</form>
</body></html>
폼 input 의 name 속성 (title, content) 이 Board 클래스 필드명과 같아서 자동 바인딩. writer 는 폼이 아니라 세션에서.
v5 는 「본인 확인」 이 없습니다. 즉 — 로그인만 했으면 누구나 누구의 글이든 수정·삭제 가능한 상태로 설계되어 있습니다 (수정·삭제는 v6 에서).
왜 일부러 위험하게? — 다음 차시 v6 에서 「본인 확인」 을 추가할 때 그 차이를 명확히 보기 위함. 「불편을 직접 느낀 다음에 도구를 만나는」 우리 학습 패턴.
| 기능 | v5 에서 | 다음에 |
|---|---|---|
| 수정·삭제 | 없음 | v6 (본인 확인 포함) |
| 페이징 | 전체 목록만 | v7 |
| 댓글 | 없음 | v9 (Ajax) |
| 조회수 자동 증가 | 표시만 (0 고정) | v10 (Ajax) |
| 검색 | 없음 | 고급 (보강) |
| 첨부파일 | 없음 | v10 |
http://localhost:8080/.../board/list — 빈 목록| 증상 | 원인 |
|---|---|
| 405 Method Not Allowed | form method="post" 빠짐 / @PostMapping ↔ method 불일치 |
| title·content 가 null | form input name ↔ VO 필드명 불일치 |
| writer 가 빈칸 | 세션에서 안 채워서 INSERT 됨 / NOT NULL 위반 |
| 새로고침 시 "재제출" 경고 | POST 후 redirect: 안 씀 |
| 목록은 비어있는데 DB 엔 있음 | 테이블명 오타 (board ≠ myboard) |
v5 는 우리 프로젝트의 「실제로 작동하는」 첫 게시판입니다.
동작은 단순하지만, 5 부품이 모두 한 번에 협업하는 첫 산출물입니다.
회원·로그인 깔끔. 게시판은 없음.
작동하는 게시판이 있다. 글 쓰고 목록 보고 상세 본다. 단 — 의도적으로 인가 없음.
myboard 등장
c:forEach 로 List 반복Board { num, title, writer, content } — 4 필드만useGeneratedKeys 로 INSERT 후 num 받기다음: ★ v6 안전한 게시판 — Update / Delete + 본인 확인.