1) SpringBoot
1-1) 복습 예제
1-1-1) 글쓰기 기능 구현
1) SpringBoot
1-1) 복습 예제
1-1-1) 글쓰기 기능 구현
(1) [src/main/resources] - [templates] 안에 home.html 파일 생성 후 아래와 같이 작성
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>web5</title>
</head>
<body>
<h1>[ web5 ]</h1>
<p sec:authorize="isAuthenticated()">
<span th:text="${#authentication.name}"></span>
님 환영합니다~~!
</p>
<div sec:authorize="not isAuthenticated()">
<p>
<a th:href="@{/member/joinForm}">회원가입</a>
</p>
<p>
<a th:href="@{/member/loginForm}">로그인</a>
</p>
</div>
<div sec:authorize="isAuthenticated()">
<p>
<a th:href="@{/member/logout}">로그아웃</a>
</p>
<p>
<a th:href="@{/member/info}">개인정보 수정</a>
</p>
</div>
<p>
<a th:href="@{/board/list}">게시판</a>
</p>
</body>
</html>
(2) [src/main/resources] - [templates.boardView] 안에 list.html 파일 생성 후 아래와 같이 작성
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>게시판</title>
</head>
<body>
<h1>[ 게시판 ]</h1>
<p>
<a th:href="@{/board/write}">글쓰기</a>
</p>
</body>
</html>
(3) [src/main/resources] - [templates.boardView] 안에 writeForm.html 파일 생성 후 아래와 같이 작성
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>글쓰기</title>
<style>
#writeArea {
width: 900px;
margin: 0 auto;
text-align: center;
}
#writeForm {
display: flex;
flex-direction: column;
justify-content: center;
}
#formArea {
display: flex;
justify-content: center;
}
table, tr, td {
border: 2px solid black;
border-collapse: collapse;
padding: 10px;
margin: 20px 0 15px 0;
}
th {
width: 100px;
background-color: grey;
color: white;
}
td {
width: 300px;
text-align: left;
}
td > .writeContent {
width: 300px;
}
td > textarea {
height: 250px;
}
</style>
<script th:src="@{/js/jquery-3.7.1.min.js}"></script>
<script>
$(document).ready(function() {
$("#writeForm").submit(check);
});
function check() {
let title = $("#title").val();
let contents = $("#contents").val();
if (title.length < 7) {
alert("제목은 최소 7자 이상으로 반드시 입력해주세요!!");
$("#title").focus();
$("#title").val('');
return false;
}
if (contents.length < 20) {
alert("내용은 20자 이상으로 반드시 입력해주세요!!");
$("#contents").focus();
$("#contents").val('');
return false;
}
return true;
}
</script>
</head>
<body>
<div id="writeArea">
<h1>[ 글쓰기 ]</h1>
<form th:action="@{/board/write}" method="post" id="writeForm">
<div id="formArea">
<table>
<tr>
<th>
<label for="title">제목</label>
</th>
<td>
<input type="text" name="title" id="title" class="writeContent" />
</td>
</tr>
<tr>
<th>
<label for="contents">내용</label>
</th>
<td>
<textarea name="contents" id="contents" class="writeContent"></textarea>
</td>
</tr>
<tr>
<th>파일첨부</th>
<td>
<input type="file" name="file" id="file" class="writeContent" />
</td>
</tr>
</table>
</div>
<div id="btnArea">
<input type="submit" value="저장" />
</div>
</form>
</div>
</body>
</html>
(4) [src/main/java] - [net.datasa.web5.security] 안에 WebSecurityConfig.java 파일 생성 후 아래와 같이 작성
package net.datasa.web5.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
//로그인 없이 접근 가능한 경로
private static final String[] PUBLIC_URLS = {
"/" //메인화면
, "/member/joinForm" //로그인 없이 접근할 수 있는 페이지
, "/member/join"
, "/member/idCheck"
, "/board/list"
, "/images/**"
, "/css/**"
, "/js/**"
};
// Bean : 메모리에 만들어진 객체(즉, 객체를 생성해서 메모리에 로드시켜 놓는 역할을 한다!)
@Bean
protected SecurityFilterChain config(HttpSecurity http) throws Exception {
http
//요청에 대한 권한 설정
.authorizeHttpRequests(author -> author
.requestMatchers(PUBLIC_URLS).permitAll() //모두 접근 허용
// 모든 요청은 인증이 필요하다는 코드이며, 바로 위의 코드만 인증 없이 모두 접근 가능하다!
.anyRequest().authenticated() //그 외의 모든 요청은 인증 필요
)
//HTTP Basic 인증을 사용하도록 설정
.httpBasic(Customizer.withDefaults())
//폼 로그인 설정
.formLogin(formLogin -> formLogin
.loginPage("/member/loginForm") //로그인폼 페이지 경로
.usernameParameter("memberId") //폼의 ID 파라미터 이름
.passwordParameter("memberPassword") //폼의 비밀번호 파라미터 이름
.loginProcessingUrl("/member/login") //로그인폼 제출하여 처리할 경로
.defaultSuccessUrl("/") //로그인 성공 시 이동할 경로
.permitAll() //로그인 페이지는 모두 접근 허용
)
//로그아웃 설정
.logout(logout -> logout
.logoutUrl("/member/logout") //로그아웃 처리 경로
.logoutSuccessUrl("/") //로그아웃 성공 시 이동할 경로
);
http
.cors(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable);
return http.build();
}
//비밀번호 암호화를 위한 인코더를 빈으로 등록
@Bean
public BCryptPasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
(5) [src/main/java] - [net.datasa.web5] 안에 Web5Application.java 파일 생성 후 아래와 같이 작성
package net.datasa.web5;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@EnableJpaAuditing
@SpringBootApplication
public class Web5Application {
public static void main(String[] args) {
SpringApplication.run(Web5Application.class, args);
}
}
(6) DB에 "web5_board" Table 생성을 위해 아래와 같이 작성
-- DB 구조
-- 게시판 글(테이블명 : web5_board)
create table web5_board(
-- 컬럼명 자료형 제약조건 설명
board_num int auto_increment primary key, -- 게시글 일련번호
member_id varchar(30), -- 작성자 ID(외래키 : web5_member 테이블 참조)
title varchar(1000) not null, -- 글제목
contents text not null, -- 글내용
view_count int default 0, -- 조회수
like_count int default 0, -- 추천수
original_name varchar(300), -- 첨부파일 원래 이름
file_name varchar(100), -- 첨부파일 저장된 이름
create_date timestamp default current_timestamp, -- 작성 시간
update_date timestamp default current_timestamp
on update current_timestamp, -- 수정 시간
constraint foreign key (member_id)
references web5_member(member_id) on delete set null
);
select * from web5_board;
insert into web5_board(title, contents, file_name) values('test1', 'test_content1', 'test_file1');
-- delete from web5_member where member_name = '김종국';
drop table web5_board;
commit;
(7) [src/main/java] - [net.datasa.web5.domain.entity] 안에 BoardEntity.java 파일 생성 후 아래와 같이 작성
- 날짜 관련 Annotation 체크사항 : Web5Application.java 파일의 "@EnableJpaAuditing", BoardEntity.java 파일의 "@EntityListeners(AuditingEntityListener.class)" / "@CreatedDate" 추가했는지 확인할 것!!
package net.datasa.web5.domain.entity;
import java.time.LocalDateTime;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name="web5_board")
@EntityListeners(AuditingEntityListener.class)
public class BoardEntity {
/*
board_num int auto_increment primary key, -- 게시글 일련번호
member_id varchar(30), -- 작성자 ID(외래키 : web5_member 테이블 참조)
title varchar(1000) not null, -- 글제목
contents text not null, -- 글내용
view_count int default 0, -- 조회수
like_count int default 0, -- 추천수
original_name varchar(300), -- 첨부파일 원래 이름
file_name varchar(100), -- 첨부파일 저장된 이름
create_date timestamp default current_timestamp, -- 작성 시간
update_date timestamp default current_timestamp
on update current_timestamp, -- 수정 시간
constraint foreign key (member_id)
references web5_member(member_id) on delete set null
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="board_num")
private Integer boardNum;
@Column(name="member_id", length = 30)
private String memberId;
@Column(name="title", nullable = false, length = 1000)
private String title;
@Column(name="contents", nullable = false, columnDefinition = "text")
private String contents;
@Column(name="view_count", columnDefinition = "int default 0")
private Integer viewCount;
@Column(name="like_count", columnDefinition = "int default 0")
private Integer likeCount;
@Column(name="original_name", length = 300)
private String originalName;
@Column(name="file_name", length = 100)
private String fileName;
// 작성 시간
@CreatedDate
@Column(name="create_date", columnDefinition = "timestamp default current_timestamp")
private LocalDateTime createDate;
// 수정 시간
@LastModifiedDate
@Column(name="update_date", columnDefinition = "timestamp default current_timestamp on update current_timestamp")
private LocalDateTime updateDate;
}
(8) [src/main/java] - [net.datasa.web5.repository] 안에 BoardRepository.java 파일 생성(반드시 Interface로 생성할 것!!) 후 아래와 같이 작성
package net.datasa.web5.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import net.datasa.web5.domain.entity.BoardEntity;
@Repository
public interface BoardRepository extends JpaRepository<BoardEntity, Integer> {
}
(9) [src/main/java] - [net.datasa.web5.domain.dto] 안에 BoardDTO.java 파일 생성 후 아래와 같이 작성
package net.datasa.web5.domain.dto;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 게시판 DTO
* */
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BoardDTO {
Integer boardNum; // 게시글 일련번호
String memberId; // 작성자 아이디(외래키)
String title; // 글 제목
String contents; // 글 내용
Integer viewCount; // 조회수
Integer likeCount; // 추천수
String originalName; // 첨부파일 원래 이름
String fileName; // 첨부파일 저장된 이름
LocalDateTime createDate ; // 작성 시간
LocalDateTime updateDate; // 수정 시간
}
(10) [src/main/java] - [net.datasa.web5.controller] 안에 BoardController.java 파일 생성 후 아래와 같이 작성
package net.datasa.web5.controller;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.datasa.web5.domain.dto.BoardDTO;
import net.datasa.web5.security.AuthenticatedUser;
import net.datasa.web5.service.BoardService;
@Slf4j
@RequestMapping("board")
@RequiredArgsConstructor
@Controller
public class BoardController {
private final BoardService boardService;
// 메인화면에서 "board/list" 경로를 클릭했을 때 처리하는 메소드
// templates/boardView/list.html 파일로 포워딩
// 로그인 안 한 상태에서 해당 페이지의 "게시판"이라는 제목이 보여야 함
/**
* 글 목록 보기
* @return 글 목록 출력 HTML 파일
* */
@GetMapping("list")
public String list() {
return "boardView/list";
}
@GetMapping("write")
public String write() {
return "boardView/writeForm";
}
@PostMapping("write")
public String join(@AuthenticationPrincipal AuthenticatedUser user,
@ModelAttribute BoardDTO boardDTO) {
log.debug("전달된 글정보 : {}", boardDTO);
boardDTO.setMemberId(user.getUsername());
log.debug("로그인한 아이디 추가한 글정보 : {}", boardDTO);
// 서비스로 전달하여 저장
boardService.saveWrite(boardDTO);
return "redirect:list";
}
}
(11) [src/main/java] - [net.datasa.web5.service] 안에 BoardService.java 파일 생성 후 아래와 같이 작성
package net.datasa.web5.service;
import org.springframework.stereotype.Service;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.datasa.web5.domain.dto.BoardDTO;
import net.datasa.web5.domain.entity.BoardEntity;
import net.datasa.web5.repository.BoardRepository;
@Slf4j
@RequiredArgsConstructor
@Service
// @Transactional : Commit, Rollback을 감지하는 역할을 하는 Annotation
@Transactional
public class BoardService {
private final BoardRepository boardRepository;
public void saveWrite(BoardDTO dto) {
BoardEntity entity = BoardEntity.builder()
// DTO로 전달받은 값들을 Entity에 세팅
.memberId(dto.getMemberId())
.title(dto.getTitle())
.contents(dto.getContents())
// 기타 추가 데이터를 Entity에 세팅
.viewCount(0)
.likeCount(0)
.build();
// DB에 저장
boardRepository.save(entity);
}
}
(12) 결과 화면
첫 접속 화면 : 게시판 리스트 화면(로그인 안해도 확인 가능) / 글쓰기 기능(로그인한 경우에만 사용 가능함)
글쓰기 화면
- 글쓰기 전
- 글쓰기 후
'SpringBoot' 카테고리의 다른 글
SpringBoot(27) - 복습 예제(게시판 글 목록 페이징 / 검색 선택 / 글 읽기) (0) | 2024.08.01 |
---|---|
SpringBoot(26) - 복습 예제(게시판 글 목록 보기) (0) | 2024.07.31 |
SpringBoot(24) - 복습 예제(개인정보 수정), 추가 정리사항(AuthenticatedUser, @Transactional) (0) | 2024.07.29 |
SpringBoot(23) - 복습 예제(비밀번호 암호화 / 로그인 / 로그아웃), 추가 정리사항(경로) (0) | 2024.07.26 |
SpringBoot(22) - 복습 예제(회원가입 시 ID 중복 체크) (0) | 2024.07.25 |