65일차 내용 정리

채공부·2025년 8월 25일

회원 정보 수정

  • UserController 에 userEdit 메소드 생성
@GetMapping("/user/edit")
public String userEdit(Model model) {
	// 로그인된 userName
	String userName = SecurityContextHolder.getContext().getAuthentication().getName();;
	UserDto dto = service.getUser(userName);
	// 응답에 필요한 정보를 Model 객체에 담기
	model.addAttribute("dto", dto);
	// 타임리프 view page 에서 회원정보 수정 폼을 응답
	return "user/edit";
}
  • edit.html 생성

  • {경로변수명}(경로변수 = 넣을값)

<div class="container">
    <h1>가입정보 수정 양식</h1>
    <form th:action="@{/user/update}" method="post" enctype="multipart/form-data" class="p-4 border rounded shadow-sm bg-light">
        <div class="mb-3">
            <label for="userName" class="form-label">아이디</label>
            <input type="text" class="form-control" id="userName" name="userName" th:value="${dto.userName}" readonly>
        </div>
        <div class="mb-3">
            <label for="email" class="form-label">이메일</label>
            <input type="email" class="form-control" id="email" name="email" th:value="${dto.email}" placeholder="이메일을 입력하세요">
        </div>
        <div class="mb-3">
            <label class="form-label d-block">프로필 이미지</label>
            <div class="text-center mb-2">
                <a href="javascript:" id="profileLink">
                    <i th:if="${dto.profileImage == null}" class="bi bi-person-circle text-secondary" style="font-size:100px;"></i>
                    <img th:unless="${dto.profileImage == null}" th:src="@{/upload/{name}(name=${dto.profileImage})}" class="rounded-circle border" style="width:100px; height:100px; object-fit:cover;">
                </a>
            </div>
            <input type="file" class="form-control d-none" name="profileFile" accept="image/*">
            <div class="form-text">클릭해서 프로필 이미지를 변경하세요.</div>
        </div>
        <div class="d-flex justify-content-between">
            <button type="submit" class="btn btn-primary">수정 확인</button>
            <button type="reset" class="btn btn-outline-secondary">취소</button>
        </div>
    </form>
</div>
<script>
	document.querySelector("#profileLink").addEventListener("click", ()=>{
    	document.querySelector("input[name=profileFile]").click();
	});
	
	document.querySelector("input[name=profileFile]").addEventListener("change", (e)=>{
		const files = e.target.files;
		const reader=new FileReader();
		reader.readAsDataURL(files[0]);
		reader.onload = ()=>{
			const img=`<img src="${reader.result}" 
			style="width:100px;height:100px;border-radius:50%">`;
			document.querySelector("#profileLink").innerHTML = img;
		};
	});
</script>
  • UserDto 에 profileImage 메소드 생성

  • <input type="file" name="profileImage"> 을 처리하기 위한 필드

private MultipartFile profileFile;
  • UserService 인터페이스에 updateUser 추상 메소드 추가
public void updateUser(UserDto dto);
  • UserServiceImpl 에 코드 추가 및 오버라이드

  • 업로드된 이미지를 저장할 위치 얻어내기

@Value("${file.location}")
private String fileLocation;
@Override
public void updateUser(UserDto dto) {
	// 업로드된 이미지가 있는지 읽어와 본다
	MultipartFile image = dto.getProfileFile();
	// 만일 업로드된 이미지가 있다면
	if(!image.isEmpty()) {
		// 원본 파일명
		String orgFileName = image.getOriginalFilename();
		// 이미지의 확장자를 유지하기 위해 뒤에 원본 파일명을 추가
		String saveFileName = UUID.randomUUID().toString() + orgFileName;
		// 저장할 파일의 전체 경로 구성하기
		String filePath = fileLocation + File.separator + saveFileName;
		try {
			// 업로드된 파일을 저장할 파일 객체 생성
			File saveFile = new File(filePath);
			image.transferTo(saveFile);
		} catch(Exception e) {
			e.printStackTrace();
		}
		// UserDto 에 저장된 이미지의 이름을 넣어준다
		dto.setProfileImage(saveFileName);
	}
    // UserDao 객체를 이용해서 수정 반영 (dto 의 profileImage 는 null 일 수도 있다)
	dao.update(dto);
}
  • UserMapper 에서 id="update" 문 확인
<update id="update" parameterType="com.example.spring08.dto.UserDto">
	UPDATE users
	SET email=#{email}, updatedAt=SYSDATE
	<if test="profileImage != null">, profileImage=#{profileImage}</if>
	WHERE userName=#{userName}
</update>
    • MyBatis 의 동적 sql 문
<if test="profileImage != null">, profileImage=#{profileImage}</if>
  • UserController 에 update 메소드 생성
@PostMapping("/user/update")
public String upate(UserDto dto) {
	// 서비스를 이용해서 개인 정보를 수정하고
	service.updateUser(dto);	
	// 개인 정보 자세히 보기로 리다일렉트 이동
	return "redirect:/user/info";
}

Board 게시판

게시글 목록

  • BoardController 클래스 생성
@Controller
public class BoardController {
	
	@GetMapping("/board/list")
	public String list(Model model) {
		return "board/list";
	}
}
  • BoardDto 클래스 생성
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class BoardDto {
	private int num;
	private String writer;
	private String title;
	private String content;
	private int viewCount;
	private String createdAt;
	// 페이징 처리를 위한 필드
	private int startRowNum;
	private int endRowNum;
	// 프로필 이미지 출력을 위한 필드
	private String profileImage;
	// 이전글, 다음글 처리를 위한 필드
	private int prevNum;
	private int nextNum;
	// 검색 키워드를 담기 위한 필드
	private String keyword;
}
  • BoardDao 인터페이스 생성
public interface BoardDao {
	public List<BoardDto> selectPage(BoardDto dto);
}
  • BoardDaoImpl 클래스 생성
  1. mapper's namespace : board
  2. sql's id : selectPage
  3. parameterType : BoardDto
  4. resultType : BoardDto
    (select 된 row 하나를 어떤 type 으로 받을지 결정 해준다)
@RequiredArgsConstructor // 의존 객체 생성자로 주입받기 위한 어노테이션 (lombok 의 기능)
@Repository
public class BoardDaoImpl implements BoardDao{
	// 의존 객체
	private final SqlSession session;

	@Override
	public List<BoardDto> selectPage(BoardDto dto) {
		return session.selectList("board.selectPage", dto);
	}
}
  • BoardMapper.xml 생성
  • application.properties 파일에
    mybatis.type-aliases-package-com.example.spring08.**
    이 설정 때문에 Dto 클래스의 이름으로 parameterType 혹은 resultType 을 간단히 작성할 수 있다
  • default 동작은 클래스명의 첫 글자를 소문자로 작성하면 된다
  • 다른 이름으로 사용하고 싶으면 @Alias("다른 이름" 를 dto 클래스에 작성해서 사용할 수 있따
<select id="selectPage" parameterType="boardDto" resultMap="boardDto">
	SELECT *
	FROM
		(SELECT result1.*, ROWNUM AS rnum
		FROM	
			(SELECT num, writer, title, viewCount, createdAt
			FROM board
			ORDER BY num DESC) result1)
	WHERE rnum BETWEEN #{startRowNum} AND #{endRowNum}	
</select>
		
<select id="selectPageByKeyword" parameterType="boardDto" resultType="boardDto">
	SELECT *
	FROM
	(SELECT result1.*, ROWNUM AS rnum
		FROM	
			(SELECT num, writer, title, viewCount, createdAt
			FROM board
			WHERE title LIKE '%' || #{keyword} || '%' OR content LIKE '%' || #{keyword} || '%'
			ORDER BY num DESC) result1)
	WHERE rnum BETWEEN #{startRowNum} AND #{endRowNum}
</select>
  • BoardDao 에 selectPageByKeyword 메소드 생성
public List<BoardDto> selectPageByKeyword(BoardDto dto);
  • BoarDaoImpl 에 오버라이드
@Override
public List<BoardDto> selectPageByKeyword(BoardDto dto) {
	return session.selectList("board.selectPageByKeyword", dto);
}
  • BoardListResponse 클래스 생성

  • 응답에 필요한 데이터를 하나의 객체에 담을 수 있도록 클래스를 설계

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class BoardListResponse {
	// 글 목록
	private List<BoardDto> list;
	private int startPageNum;
	private int endPageNum;
	private int totalPageCount;
	private int pageNum;
	private int totalRow;
	private String keyword;	
}
  • BoardDao 에 getCount & getCountByKeyword 메소드 추가
public int getCount();
public int getCountByKeyword(String keyword);
  • BoardDaoImpl 에 추상 메소드 오버라이드
@Override
public int getCount() {
	// resultType : int
	return session.selectOne("board.getCount");
}

@Override
public int getCountByKeyword(String keyword) {
	/*
	 * parameterType : string
	 * resultType : int
	 * */
	return session.selectOne("board.getCountByKeyword", keyword);
}
  • BoardMapper 에 추가
<select id="getCount" resultType="int">
	SELECT MAX(ROWNUM) AS count
	FROM board
</select>
	
<select id="getCountByKeyword" parameterType="string" resultType="int">
	SELECT MAX(ROWNUM) AS count
	FROM board
	WHERE title LIKE '%' || #{keyword} || '%' OR content LIKE '%' || #{keyword} || '%'
</select>
  • BoardService 인터페이스 생성
public interface BoardService {
	public BoardListResponse getBoardList(int pageNum, String keyword);
}
  • BoardServiceImpl 클래스 생성
@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService{
	
	private final BoardDao boardDao;
	
	// pageNum 또는 keyword 에 해당하는 글목록과 추가 정보를 BoardListResponse 객체에 담아서 
	// 리턴하는 메소드 
	@Override
	public BoardListResponse getBoardList(int pageNum, String keyword) {
		
		//한 페이지에 몇개씩 표시할 것인지
		final int PAGE_ROW_COUNT=3;
		
		//하단 페이지를 몇개씩 표시할 것인지
		final int PAGE_DISPLAY_COUNT=3;

		//보여줄 페이지의 시작 ROWNUM
		int startRowNum=1+(pageNum-1)*PAGE_ROW_COUNT; //공차수열
		//보여줄 페이지의 끝 ROWNUM
		int endRowNum=pageNum*PAGE_ROW_COUNT; //등비수열 
		
		//하단 시작 페이지 번호 (정수를 정수로 나누면 소수점이 버려진 정수가 나온다)
		int startPageNum = 1 + ((pageNum-1)/PAGE_DISPLAY_COUNT)*PAGE_DISPLAY_COUNT;
		//하단 끝 페이지 번호
		int endPageNum=startPageNum+PAGE_DISPLAY_COUNT-1;
		
		/*
			StringUtils 클래스의 isEmpty() static 메소드를 이용하면 문자열이 비었는지 여부를 알수 있다
			null 또는 "" 빈문자열은  비었다고 판정된다.
			
			StringUtils.isEmpty(keyword)
			는
			keyword == null or "".equals(keyword) 
			를 대체할수 있다.
		*/
		//전체 글의 갯수 
		int totalRow=0;
		//만일 전달된 keyword 가 없다면 
		if(StringUtils.isEmpty(keyword)){
			totalRow=boardDao.getCount();
		}else{ //있다면 
			totalRow=boardDao.getCountByKeyword(keyword);
		}
		
		//전체 페이지의 갯수 구하기
		int totalPageCount=(int)Math.ceil(totalRow/(double)PAGE_ROW_COUNT);
		//끝 페이지 번호가 이미 전체 페이지 갯수보다 크게 계산되었다면 잘못된 값이다.
		if(endPageNum > totalPageCount){
			endPageNum=totalPageCount; //보정해 준다. 
		}	
		
		//dto 에 select 할 row 의 정보를 담고 
		BoardDto dto=new BoardDto();
		dto.setStartRowNum(startRowNum);
		dto.setEndRowNum(endRowNum);
		//글목록 
		List<BoardDto> list=null;
		//만일 keyword 가 없아면 
		if(StringUtils.isEmpty(keyword)){
			list = boardDao.selectPage(dto);
		}else{//있다면
			// dto 에 keyword 를 담고 
			dto.setKeyword(keyword);
			// 키워드에 해당하는 글 목록을 얻어낸다 
			list = boardDao.selectPageByKeyword(dto);
		}
		// 한줄 coding 으로 BoardListResponse 객체를 만들어서 리턴
		return BoardListResponse.builder()
				.list(list)
				.pageNum(pageNum)
				.keyword(keyword)
				.startPageNum(startPageNum)
				.endPageNum(endPageNum)
				.totalPageCount(totalPageCount)
				.totalRow(totalRow)
				.build();
	}
}
  • BoardController 에 list 메소드 코드 추가 및 서비스 주입

  • @RequestParam 어노테이션을 이용하면 요청 파라미터를 추출하면서 해당 값이 없으면 defaultValue 설정 가능

@RequiredArgsConstructor
@Controller
public class BoardController {
	
	private final BoardService service;
	
	@GetMapping("/board/list")
	public String list(Model model,
			@RequestParam(defaultValue = "1") int pageNum,
			@RequestParam(defaultValue = "") String keyword) {
		// 응답에 필요한 데이터를 얻어내서
		BoardListResponse listResponse = service.getBoardList(pageNum, keyword);
		// 모델 객체에 담고
		model.addAttribute("listResponse", listResponse);
		// 타임리프 페이지에서 응답
		return "board/list";
	}
} 

게시글 작성

  • board 폴더 생성 후 그 안에 list.html 생성

  • GET 방식 파라미터 : (파라미터명 = 값)
    ex. (num=${tmp.num})

<th:block th:insert="/include/navbar :: nav('board')"></th:block>
<div class="container pt-1">
    <a class="btn btn-outline-primary btn-sm" th:href="@{/board/new-form}">
        새 글 작성 <i class="bi bi-pencil-square"></i>
    </a>
    <h1>게시글 목록 입니다</h1>
    <!-- 행을 만들고 -->
    <div class="row my-3">
        <!-- 4/12 인 칼럼을 만들고 margin-start:auto 를 부여해서 왼쪽 마진을 자동 부여한다 -->
        <div class="col-lg-4 col-md-6 ms-auto">
            <form th:action="@{/board/list}" method="get">
                <div class="input-group">
                    <input th:value="${#strings.isEmpty(dto.keyword) ? '' : dto.keyword}" type="text" name="keyword" class="form-control" placeholder="검색어 입력..."/>
                    <button type="submit" class="btn btn-outline-secondary bg-primary-subtle btn-sm">
                        <i class="bi bi-search"></i>
                    </button>
                </div>
            </form>
        </div>
    </div>
    <table class="table table-bordered">
        <thead>
            <tr> 
                <th>글번호</th>
                <th>작성자</th>
                <th>제목</th>
                <th>조회수</th>
                <th>작성일</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="tmp : ${dto.list}">
                <td th:text="${tmp.num}"></td>
                <td th:text="${tmp.writer}"></td>
                <td>
                    <a th:href="@{/board/view(num=${tmp.num})}" th:text="${tmp.title}"></a>
                </td>
                <td th:text="${tmp.viewCount}"></td>
                <td th:text="${tmp.createdAt}"></td>
            </tr>
        </tbody>
    </table>
    <ul class="pagination">
        <li th:if="${dto.startPageNum ne 1}" class="page-item">
            <a class="page-link"
                th:href="@{/board/list(num=${dto.startPageNum-1}, keyword=${dto.keyword})}">&lsaquo;</a>
        </li>        
        <li th:each="i : ${#numbers.sequence(dto.startPageNum, dto.endPageNum)}" class="page-item">
            <a class="page-link" th:classappend="${i eq dto.pageNum ? 'active' : null}" 
                th:href="@{/board/list(pageNum=${i}, keyword=${dto.keyword})}" th:text="${i}">
            </a>
        </li>
        <li th:id="${dto.endPageNum lt dto.totalPageCount}" class="page-item">
            <a class="page-link"
                th:href="@{/board/list(pageNum=${dto.endPageNum+1}, keyword=${dto.keyword})}">&rsaquo;</a>
        </li>
    </ul>
</div>
  • BoardMapper 에 id=getCount & id=getCountByKeyword 코드 수정
<select id="getCount" resultType="int">
	SELECT NVL(MAX(ROWNUM), 0) AS count
	FROM board
</select>
	
<select id="getCountByKeyword" parameterType="string" resultType="int">
	SELECT NVL(MAX(ROWNUM), 0) AS count
	FROM board
	WHERE title LIKE '%' || #{keyword} || '%' OR content LIKE '%' || #{keyword} || '%'
</select>
  • BoardController 에 newForm 메소드 생성
@GetMapping("/board/new-form")
public String newForm() {
	return "board/new-form";
}
  • resource.html 에 toastui 추가
<th:block th:fragment="toastui">
	<link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.min.css" />
	<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
	<script src="https://uicdn.toast.com/editor/latest/i18n/ko-kr.js"></script>
</th:block>
  • new-form.html 생성
<div class="container">
	<h1>게시글 작성 양식</h1>
	<form th:action="@{/board/save}" method="post" id="saveForm">
		<div class="mb-2">
			<label class="form-label" for="title">제목</label>
			<input class="form-control" type="text" name="title" id="title" />
		</div>
		<div class="mb-2">
			<label class="form-label" for="editor">내용</label>
			<!-- Editor ui 이 출력할 div -->
			<div id="editor"></div>
				
			<textarea class="form-control" name="content" id="hiddenContent" ></textarea>
		</div>
		<button class="btn btn-sm bg-primary-subtle text-white" type="submit">작성하기</button>
	</form>
</div>
<script>
	// 위에 toast ui javascript 가 로딩되어 있으면 toastui.Editor 클래스를 사용할 수 있다
	// 해당 클래스를 이용해서 객체 생성하면서 {} object 로 ui 에 관련된 옵션을 잘 전달하면
	// 우리가 원하는 모양의 텍스트 편집기를 만들 수 있다
	const editor = new toastui.Editor({
		el:document.querySelector('#editor'),
		height:'500px',
		initialEditType:'wysiwyg',
		previewStyle:'vertical',
		language:'ko'
	});
	document.querySelector("#saveForm").addEventListener("submit", (e)=>{
		// 에디터로 작성된 문자열 읽어오기
		const content = editor.getHTML();
		// 테스트로 콘솔에 출력하기
		console.log(content);
		// 에디터로 작성된 문자열을 폼 전송이 될 수 있도록 textarea 의 value 로 넣어준다
		document.querySelector("#hiddenContent").value = content;
				
		// 테스트 하기 위해 폼 전송 막기
		// e.preventDefault();
	});
</script>
  • SecurityConfig 에 화이트리스트 추가
String[] whiteList = {"/", "/user/loginform", "/user/login-fail",
				"/user/expired", "/user/signup-form", "/user/signup",
				 "/test/**", "/user/check-id", "/board/list", "/board/view"};
  • BoardController 에 boardSave 메소드 생성
@PostMapping("/board/save")
public String boardSave(BoardDto dto) {
	// 글 작성자
	String userName = SecurityContextHolder.getContext().getAuthentication().getName();
	dto.setWriter(userName);
	// 서비스를 이용해서 글을 저장하기
    
	return "board/save";
}
  • BoardService 에 createContent 메소드 생성
public void createContent(BoardDto dto);
  • BoardMapper 에 id="insert" 문 추가
  • 아래와 같이 insert 를 구성하면
    1. insert 문이 실행되기 전에 시퀀스 값을 미리 읽어온다
    2. parameterType 으로 전달된 boardDto 객체에 담긴다
    3. 그 다음 insert 문이 실행되기 때문에 #{num} 에는 시퀀스으로 얻어낸 값이 들어간다
<insert id="insert" parameterType="boardDto">
	<selectKey keyProperty="num" resultType="int" order="BEFORE">
		SELECT board_seq.NEXTVAL FROM DUAL
	</selectKey>
	INSERT INTO board
	(num, writer, title, content)
	VALUES(#{num}, #{writer}, #{title}, #{content})
</insert>
  • BoardDao 에 insert 메소드 생성
public void insert(BoardDto dto);
  • BoardDaoImpl 에 추상 메소드 오버라이드
  • 이 메소드를 호출하는 시점에 dto.num 은 0 이지만
  • 이 메소드가 리턴된 이후에는 dto.num 에는 저장된 글번호가 들어 있다
    (selectKey 의 결과값이 들어 있다)
@Override
public void insert(BoardDto dto) {
	session.insert("board.insert", dto);
}
  • BoardServiceImpl 에 추상 메소드 오버라이드

  • 글 정보가 저장된 이후에는 dto.num 에 글번호가 들어 있다

@Override
public void createContent(BoardDto dto) {
	boardDao.insert(dto);
}
  • BoardController 에 boardSave 메소드 완성

  • @ModelAttribute : view page 에서 필요한 값을 대신 Model 객체에 담아준다

@PostMapping("/board/save")
public String boardSave(@ModelAttribute("dto") BoardDto dto) {
	// 글 작성자
	String userName = SecurityContextHolder.getContext().getAuthentication().getName();
	dto.setWriter(userName);
	// 서비스를 이용해서 글을 저장하기
	service.createContent(dto);
	return "board/save";
}
  • save.html 생성
<div class="container">
	<script>
		alert("저장 완료");
		location.href = "[[@{/board/view(num=${dto.num})}]]";
	</script>
</div>

게시글 상세보기

  • boardMapper 에 id="getByNum"문 생성
<select id="getByNum" parameterType="int" resultMap="boardDto">
	SELECT *
	FROM	
		(SELECT b.num, writer, title, content, viewCount, 
			TO_CHAR(b.createdAt, 'YY"년" MM"월" DD"일" HH24:MI') AS createdAt, 
			profileImage,
			LAG(b.num, 1, 0) OVER (ORDER BY b.num DESC) AS prevNum,
			LEAD(b.num, 1, 0) OVER (ORDER BY b.num DESC) AS nextNum
		FROM board b
		INNER JOIN users u ON b.writer = u.userName) 
	WHERE num=#{num}
</select>
  • BoardDao 에 getByNum 메소드 생성
public BoardDto getByNum(int num);
  • BoardDaoImpl 에 추상 메소드 오버라이드
@Override
public BoardDto getByNum(int num) {
	return session.selectOne("board.getByNum", num);
}
  • CommentDto 클래스 생성
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class CommentDto {
	private int num;
	private String writer;
	private String content;
	private String targetWriter;
	private int groupNum;
	private int parentNum;
	private String deleted;
	private String createdAt;
	private String profileImage; //프로필 이미지를 출력하기 위한 필드 
	//대댓글의 갯수를 저장하기 위한 필드
	private int replyCount;
}
  • CommentMapper.xml 생성
<mapper namespace="comment">
	<select id="selectList" parameterType="int" resultType="commentDto">
		<![CDATA[
			SELECT c.num, c.writer, c.targetWriter, c.content, c.deleted, c.groupNum, u.profileImage,
				CASE
					WHEN SYSDATE - c.createdAt < 1/1440 THEN '1분 전'
					WHEN SYSDATE - c.createdAt < 10/1440 THEN '10분 전'
					WHEN SYSDATE - c.createdAt < 365 THEN
						TO_CHAR(TRUNC(SYSDATE - c.createdAt)) || '일 전'
					WHEN SYSDATE - c.createdAt < 730 THEN '1년 전'
					ELSE '2년 이상'
				END AS createdAt,
				(SELECT COUNT(*)
				FROM comments
				WHERE groupNum = c.num AND num != groupNum) AS replyCount				
			FROM comments c
			INNER JOIN users u ON c.writer = u.userName
			WHERE c.parentNum = #{num}
			ORDER BY c.groupNum ASC, c.num ASC
		]]>	
	</select>
</mapper>
  • CommentDao 인터페이스 생성
public interface CommentDao {
	public List<CommentDto> selectList(int parentNum);
}
  • CommentDaoImpl 클래스 생성 및 오버라이드
@RequiredArgsConstructor
@Repository
public class CommentDaoImpl implements CommentDao{

	private final SqlSession session;
	// 원글의 글 번호를 이용해서 원 글에 달린 댓글 목록을 리턴하는 메소드
	@Override
	public List<CommentDto> selectList(int parentNum) {
		return session.selectList("comment.selectList", parentNum);
	}

}
  • BoardServiceImpl 에 의존 주입

  • 하나의 서비스에서 여러개의 dao 에 의존하는 경우도 존재

private final CommentDao commentDao;
  • BoardService 에 getDetail & getComments 메소드 생성
public BoardDto getDetail(int num);
public List<CommentDto> getComments(int parentNum);
  • BoardServiceImpl 에 오버라이드
@Override
	public BoardDto getDetail(int num) {

		return boardDao.getByNum(num);
	}

	@Override
	public List<CommentDto> getComments(int parentNum) {

		return commentDao.selectList(parentNum);
	}
  • BoardController 에 boardView 메소드 생성
@GetMapping("/board/view")
public String boardView(int num, Model model) {
	// 서비스를 이용해서 응답에 필요한 데이터를 얻어내기
	BoardDto dto = service.getDetail(num);
	List<CommentDto> comments = service.getComments(num);
	// 모델 객체에 담고
	model.addAttribute("dto", dto);
	model.addAttribute("commentList", comments);
	// 로그인된 userName 얻어내기
	// 로그인 안 했으면 "annontmousUser" 가 리턴
	String userName = SecurityContextHolder.getContext().getAuthentication().getName();
	boolean isLogin = userName.equals("annoymousUser") ? false:true;
	// 위의 추가 정보고 모델 객체에 담는다
	model.addAttribute("userName", userName);
	model.addAttribute("isLogin", isLogin);
	// 타임리프 페이지에서 응답
	return "board/view";
}
  • view.html 생성
이거 타임리프로 해야함 집에서..~
profile
학원 공부 내용 정리

0개의 댓글