게시판을 만들다가 이상한 점을 찾았다.
구글 소셜 로그인을 하면 그 로그인 사용자의 이름이 노출되고 logout 버튼이 나와야하는데 나는 아무리 해도 Google Login 버튼밖에 노출되지 않았다.
➡️ 로그인이 된 상태인지 안 된 상태인지 구분 불가
그래서 코드를 하나하나 살펴보고 로그인이 된 상태인지 DB에서 정보를 삭제하고, 다시 로그인을 해봤는데 동작은 정상적으로 되고 있었다.
그래서 다시 코드를 한 글자 한 글자씩 다 비교해보니까 드디어 어느 부분이 잘못된건지 찾을 수 있었다.
IndexController에서
@RequiredArgsConstructor
@Controller
public class IndexController {
private final PostsService postsService;
private final HttpSession httpSession;
@GetMapping("/")
public String index(Model model) {
model.addAttribute("posts", postsService.findAllDesc());
SessionUser user = (SessionUser) httpSession.getAttribute("user");
if(user != null) {
model.addAttribute("userName", user.getName());
}
return "index";
}
model.addAttribute("userName",
➡️ 이 부분이 username으로 되어있었다. userName인데,,
이렇게 사용자 이름이 노출되는 것이 정상이다.
해결 완료!!
또 다른 문제가 생겼다.
글 등록 버튼을 누르면 글을 등록할 수 있는 화면이 나와야 하는데 에러 페이지가 뜬다.
그래서 이 부분도 마찬가지로 잘못된 부분을 찾으려고 Controller부터 싹 점검했는데 찾을 수가 없었다.
그러던 와중에 갑자기 인텔리제이가 말을 안 들어서 껐다 켰는데 갑자기 되네 🤷🏻♀️
게시글 작성시 제목, 작성자, 내용 모두 비워도 예외 처리를 하지 않았기 때문에 아무것도 작성하지 않고 등록해도 글이 등록된다.
그래서 목록에서 확인해보면 제목이 없어서 그 글로 들어가 수정하거나, 삭제할 수가 없다.
(DB에서 직접 삭제해줘야 한다.)
그래서 자동으로 생성되는 게시글 번호를 이용해서 글에 접근할 수 있도록 코드를 수정했다.
{{#posts}}
<tr>
<!-- <td><a href="/posts/update/{{id}}">{{id}}</td>-->
<td>{{id}}</td>
<td><a href="/posts/update/{{id}}">{{title}}</a></td>
<td>{{author}}</td>
<td>{{modifiedDate}}</td>
</tr>
{{/posts}}
<td>{{id}}</td>
➡️ <td><a href="/posts/update/{{id}}">{{id}}</td>
그리고 삭제 기능을 추가했다.
PostsApiController.java
@DeleteMapping("/api/posts/{id}")
public Long delete(@PathVariable Long id) {
postsService.delete(id);
return id;
}
PostsService.java
@Transactional
public void delete(Long id) {
Posts posts = postsRepository.findById(id)
.orElseThrow(
() -> new IllegalArgumentException("해당 게시글이 없습니다. id=" + id)
);
postsRepository.delete(posts);
}
이와 같이 코드를 추가한다.
그리고 저번 글에는 화면 구현을 안 했는데 저번에 책 보면서 실습했던 화면을 가져다가 썼다.
🐱 깃허브
현재 mysql posts 테이블 구성은 아래와 같다.
테이블 컬럼(필드) 조회: show full columns from [테이블명];
그래서 파일 업로드 기능을 추가하기 위해 세 가지 필드를 추가했다.
필드 추가: alter table [테이블명] add [추가할 컬럼명] [데이터타입] after [어떤 필드 뒤에 추가할건지]
(after는 필수 사항은 아니다.)
이런식으로 필드를 추가해준다.
➡️ 최종 posts 테이블 컬럼(필드)
스프링 프레임워크에는 파일 업로드를 위한 MultipartResolver 인터페이스가 정의되어 있다.
일반적으로 사용되는 MultipartResolver 인터페이스의 구현체는
CommonMultipartResolver
StandardServletMultipartResolver
여기서는 CommonMultipartResolver 사용한다.
참고로
multipartResolver
는 maxUploadSize(최대크기), maxInMemorySize(임시 파일 생성 전 메모리 보관 최대 크기), defaultEncoding(인코딩 타입) 세 가지 속성을 지원한다.
아래와 같이 코드 추가
WebConfig
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setDefaultEncoding("UTF-8");
commonsMultipartResolver.setMaxUploadSizePerFile(5 * 1024 * 1024);
return commonsMultipartResolver;
}
application 코드를 아래와 같이 변경
@EnableJpaAuditing
@SpringBootApplication(exclude = {MultipartAutoConfiguration.class})
public class YoonProjectApplication {
public static void main(String[] args) {
SpringApplication.run(YoonProjectApplication.class, args);
}
}
posts-save.mustache 코드 추가 <form> 부분
</form>
<form id="frm" name="frm" method="post" action="/api/insertBoard.do" enctype="multipart/form-data">
<input type="file" id="files" name="files" multiple="multiple">
<input type="submit" id="submit" value="저장" class="btn">
</form>
<a href="/" role="button" class="btn btn-secondary">취소</a>
<button type="button" class="btn btn-primary" id="btn-save">등록</button>
web/dto/PostsFileDto
@Data
public class PostsFileDto {
private int idx;
private int boardIdx;
private String originalFileName;
private String storedFilePath;
private long fileSize;
}
PostsSqveRequestDto에 파일 추가 일단 보류
PostsApiController → 메소드 수정
PostsService → 메소드 수정
컨트롤러, 서비스 다시 수정해야하고(save쪽), repository interface도 수정해야 될 수도
현재 진행상황
파일 등록 버튼은 잘 만들어졌고,
➡️ 임시로 등록말고 업로드가 되는지 확인하려고 (원래는 안되지만) system.out으로 찍어보았다.
메소드가 잘 작동하는 것은 확인했지만,
원하지도 않는 다운로드가 되어버린다. 🤷🏻♀️(PostService.java, PostsApiController.java, posts-save 수정 중)
📌 확장자 .do 파일
: jsp에서 사용하는 가상의 주소
어노테이션을 이용하여 가상의 주소를 받아 자바 파일에서 처리한다.
페이지를 돌려줄 때 get 또는 post 방식으로 원하는 jsp 파일을 호출한다.가상의 주소를 사용하면 왜 좋은가?
사용자는 파일의 실제 경로를 알 수 없으므로 보안에 도움이 된다.
➡️ 소스보기를 해도 소스는 볼 수 있지만 파일의 이름과 경로는 알 수 없음흐름은 어떻게 되는가?
test.jsp 페이지에서 test.do가 링크 되어있는 요소를 클릭하면
→ 이동하는 페이지는 웹 서버에서 test.do가 있는 java 파일을 확인하고
→ test.do를 사용하는 어노테이션을 가진 메소드로 이동해 처리.
→ 이후 메소드 안에서 지정해놓은 jsp 파일로 이동어떻게 사용하는가?
@Controller public class Controller { @RequestMapping("test.do") // test.do를 처리하는 메소드가 됨 public void helloTest() { // 원하는 처리 ... // 반환할 페이지를 정의 } }
(2/8) 새로운 방법으로 시도해보았다.
그리고 Posts와 PostsSaveRequestDto에도 필드를 추가해주었다. (@Builder
쪽도 수정)
PostsApiController와 PostsService도 수정해보았다.
// @RequestMapping("/api/insertfile")
// public String insertFile(HttpServletRequest request, @RequestPart MultipartFile files) throws Exception {
//// Posts posts = new Posts();
// PostsSaveRequestDto postsSaveRequestDto = new PostsSaveRequestDto();
//
// String sourceFileName = postsSaveRequestDto.getFileOriName();
// String sourceFileNameExtension = FilenameUtils.getExtension(sourceFileName).toLowerCase();
// PostsSaveRequestDto destinationFile;
// String destinationFileName;
// String fileUrl = "/Users/hayoonkyung/SpringBootStudy/SpringBoot/yoon-project/src/main/resources/static/images/";
//
//// do {
// destinationFileName = RandomString.make(32) + "." + sourceFileNameExtension;
//// destinationFile = new PostsSaveRequestDto(fileUrl + destinationFileName);
//// } while(destinationFile.exists());
//
//// destinationFile.getParnetFile().mkdirs();
//
// postsSaveRequestDto.setFileName(destinationFileName);
// postsSaveRequestDto.setFileOriName(sourceFileName);
// postsSaveRequestDto.setFileUrl(fileUrl);
//
// postsService.save(postsSaveRequestDto);
// return "redirect:/";
// }
// public void save(PostsSaveRequestDto requestDto) {
// PostsSaveRequestDto p = new PostsSaveRequestDto();
//
// p.setFileName(requestDto.getFileName());
// p.setFileOriName(requestDto.getFileOriName());
// p.setFileUrl(requestDto.getFileUrl());
//
// postsRepository.save(p);
//
// }
이제 문제는 에러는 없지만, 파일 등록이라는 기능을 추가했는지 쥐도 새도 모른다.
결국 파일을 함께 등록 해봤자 글만 등록된다.
이젠 나도 모르겠다 ^^!
@RequestPart
: JSON 파일로 넘어온 데이터를 바인딩 할 수 있음
맥북 파일 경로 복사 단축키
Command + Option + C
참고한 사이트