게시글 삭제 API와 파일 업로드 기능 추가하기 (+에러수정)


문제 1

게시판을 만들다가 이상한 점을 찾았다.
구글 소셜 로그인을 하면 그 로그인 사용자의 이름이 노출되고 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인데,,

이렇게 사용자 이름이 노출되는 것이 정상이다.
해결 완료!!


문제 2

또 다른 문제가 생겼다.

글 등록 버튼을 누르면 글을 등록할 수 있는 화면이 나와야 하는데 에러 페이지가 뜬다.

그래서 이 부분도 마찬가지로 잘못된 부분을 찾으려고 Controller부터 싹 점검했는데 찾을 수가 없었다.
그러던 와중에 갑자기 인텔리제이가 말을 안 들어서 껐다 켰는데 갑자기 되네 🤷🏻‍♀️


문제 3

게시글 작성시 제목, 작성자, 내용 모두 비워도 예외 처리를 하지 않았기 때문에 아무것도 작성하지 않고 등록해도 글이 등록된다.

그래서 목록에서 확인해보면 제목이 없어서 그 글로 들어가 수정하거나, 삭제할 수가 없다.
(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 인터페이스의 구현체

  • apache의 Common Fileupload를 이용한 CommonMultipartResolver
  • 서블릿 3.0 이상의 API를 이용한 StandardServletMultipartResolver

여기서는 CommonMultipartResolver 사용한다.

참고로 multipartResolvermaxUploadSize(최대크기), 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도 수정해보았다.

PostsApiController.java

//    @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:/";
//    }

PostsService.java

//    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

참고


참고한 사이트

파일업로드 참고
mysql 테이블 컬럼 추가 참고
.do 확장자 참고

좋은 웹페이지 즐겨찾기