Loading...

Spring/Tistory / / 2022. 4. 27. 00:02

블로그-V3. 글쓰기 화면(Quill, 카테고리, 파일 업로드)

반응형

글쓰기 버튼 만들어주고 글쓰기 화면 만들건데 퀼 에디터 써보자.

 

https://quilljs.com/

 

Quill - Your powerful rich text editor

Sailec Light Sofia Pro Slabo 27px Roboto Slab Inconsolata Ubuntu Mono Quill Rich Text Editor Quill is a free, open source WYSIWYG editor built for the modern web. With its modular architecture and expressive API, it is completely customizable to fit any ne

quilljs.com

 

 

 

글쓰기 화면에서는 드로우바가 필요 없으니까 main-header 사용할 것이다.

여기에 링크 추가해주자.

 

<!-- Include stylesheet -->
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">

 

글쓰기 화면 mustache를 만들어서 메인 스크립트를 넣어준다.

 

{{>/layout/main-header}}

<style>
    .ql-editor {
        min-height: 40vh;
    }
</style>


<div class="container">

    <form action="/s/post" method="post" enctype="multipart/form-data" onsubmit="return getQuill()">
        <input type="text" placeholder="Enter Title" name="title" class="form-control">

		<!-- 툴 설정 -->
        <div id="toolbar-container">
            <span class="ql-formats">
                <select class="ql-font"></select>
                <select class="ql-size"></select>
            </span>
            <span class="ql-formats">
                <button class="ql-bold"></button>
                <button class="ql-italic"></button>
                <button class="ql-underline"></button>
                <button class="ql-strike"></button>
            </span>
            <span class="ql-formats">
                <select class="ql-color"></select>
                <select class="ql-background"></select>
            </span>
            <span class="ql-formats">
                <button class="ql-script" value="sub"></button>
                <button class="ql-script" value="super"></button>
            </span>
            <span class="ql-formats">
                <button class="ql-header" value="1"></button>
                <button class="ql-header" value="2"></button>
                <button class="ql-blockquote"></button>
            </span>
            <span class="ql-formats">
                <button class="ql-list" value="ordered"></button>
                <button class="ql-list" value="bullet"></button>
                <button class="ql-indent" value="-1"></button>
                <button class="ql-indent" value="+1"></button>
            </span>
            <span class="ql-formats">
                <button class="ql-direction" value="rtl"></button>
                <select class="ql-align"></select>
            </span>
            <span class="ql-formats">
                <button class="ql-link"></button>
                <button class="ql-image"></button>
                <button class="ql-video"></button>
            </span>
            <span class="ql-formats">
                <button class="ql-clean"></button>
            </span>
        </div>

        <div id="editor-container"></div>
        <textarea name="content" id="content" class="my_hidden">

        </textarea>
        <div class="form-control d-flex justify-content-end">
            <div>섬네일 사진 등록 : <input type="file" name="thumnailFile"></div>
        </div>
        <button type="submit" class="my_active_btn">글쓰기 등록</button>
    </form>
    <br />

</div>

<script>
    function getQuill() {
        let quillContent = $("#editor-container .ql-editor").html();
        $("#content").html(quillContent);
        return true;
    }
</script>

<!-- Include the Quill library -->
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>

<script>
    var quill = new Quill('#editor-container', {
        modules: {
            formula: true,
            syntax: true,
            toolbar: '#toolbar-container'
        },
        placeholder: '게시물을 작성해주세요.',
        theme: 'snow'
    });
</script>


{{>/layout/footer}}

 

글쓰기 화면으로 이동하는 컨트롤러 생성해주면 된다.

/s가 붙었기 때문에 시큐리티가 알아서 인증 체크를 해준다.

코드가 깔끔해진다.

 

시큐리티를 사용하지 않으면 인터셉터를 사용하면 된다!!

 

@GetMapping("/s/post/write-form")
public String writeForm() {
    return "/post/writeForm";
}

 

여러 가지 타입을 전송할 때 multipart/form-data 타입을 사용한다.

Dto를 만들어서 파일을 잘 받았는지 확인해보자.

 

우리는 name="thumnail"로 이미지를 받았는데 이 파일은 하드디스크에 저장하고,

DB에는 저장 경로를 저장해야 한다.

 

이름이 잘못된 것 같으니까 thumnailFile로 이름을 바꿔주자.

 

 

Valid 체크해줄 건데 title에만 걸어주면 되겠다.

content와 썸네일은 null이 허용이기 때문이다.

 

하지만 @NotNull은 체크해주자.

공백이 들어와도 되긴 하지만 키 값 자체를 안보내면 안되기 때문이다.

 

package site.metacoding.blogv3.web.dto.post;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.springframework.web.multipart.MultipartFile;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class PostWriteReqDto {

    @NotBlank
    @Size(min = 1, max = 60)
    private String title;

    @NotNull // 공백은 가능한데 키값은 전송하세요
    private MultipartFile thumnailFile; // 썸네일은 null 허용

    @NotNull // 공백은 가능한데 키값은 전송하세요
    private String content; // 컨텐트 null 허용
}

 

컨트롤러 만들어주자.

성공적으로 데이터를 받으면 해당 유저의 블로그 페이지를 돌려주면 되겠다.

 

@PostMapping("/s/post")
public String write(PostWriteReqDto postWriteReqDto,
        @AuthenticationPrincipal LoginUser loginUser) {

    return "redirect:/user/" + loginUser.getUser().getId() + "/post";
}

 

div 태그로 만들어준 quill 에디터는 내부가 html로 자동 변환된다.

div 태그는 form태그로 전송이 안된다!!

 

textarea안에는 html이 먹지 않기 때문에 content가 전송되지 않는다.

div 태그의 id가 editor-container인데

이 내부의 내용을 textarea안에 집어넣는 함수를 만들어줄 것이다.

 

<script>
    function getQuill() {
        let content = $("#editor-container").html();
        console.log(content);
    }
</script>

 

그리고 이 textarea는 보이지 않게 my_hidden 클래스를 걸어주고 name에 content를 달아줘서

얘를 전송해줄 것이다.

 

잘 담기는지 테스트해보자.

 

잘 들어온다.

 

 

테스트해보면 에디터의 hidden값까지 찾아온다.

이 내부의 ql-editor 클래스의 값을 찾아와야 한다.

 

css 선택자는 한 칸 띄우면서 내부를 찾아간다.

$("#editor-container .ql-editor").html( );

 

ql-editor는 클래스니까 .으로 찾아야 한다.

찾은 이 값을 textarea의 html로 넣어주면 끝!

 

<script>
    function getQuill() {
        let quillContent = $("#editor-container .ql-editor").html();
        $("#content").html(quillContent);
        return true;
    }
</script>

 

이렇게 값을 끌어오지 않으면 div의 값이 전송되지 않기 때문에 끌어온 것이다.

디버깅 모드로 확인해보니 잘 들어왔다.

 

 

이제 진짜 DB에 넣으면 끝이다.

 

 

 

카테고리 아이디를 잘 받았다.

 

writeForm을 갈 때 카테고리 데이터를 모델에 담아 가야 한다.

이때 세션 값을 알아야 카테고리를 가져갈 수 있다.

카테고리의 모델을 보면 userId가 있기 때문에 카테고리를 찾을 수 있다.

 

카테고리의 데이터를 가져오는 것이지 카테고리 서비스가 아니다.

포스트 서비스에서 만들어줘야 한다.

 

PostService ▼

public List<Category> 게시글쓰기화면(User principal) {
    return categoryRepository.findByUserId(principal.getId());
}

 

PostController ▼

@GetMapping("/s/post/write-form")
public String writeForm(@AuthenticationPrincipal LoginUser loginUser, Model model) {
    List<Category> categorys = postService.게시글쓰기화면(loginUser.getUser());

    if (categorys.size() == 0) {
        throw new CustomException("카테고리 등록이 필요해요");
    }

    model.addAttribute("categorys", categorys);
    return "/post/writeForm";
}

 

 

ssar이 가지고 있던 카테고리가 잘 나온다.

 

카테고리가 없는 유저들은 글을 쓸 수 없게

카테고리 생성 페이지로 redirection 해줘야 한다.

 

 

 

[출처]

 

https://cafe.naver.com/metacoding

 

메타코딩 : 네이버 카페

코린이들의 궁금증

cafe.naver.com

메타 코딩 유튜브

https://www.youtube.com/c/%EB%A9%94%ED%83%80%EC%BD%94%EB%94%A9

 

메타코딩

문의사항 : getinthere@naver.com 인스타그램 : https://www.instagram.com/meta4pm 깃헙 : https://github.com/codingspecialist 유료강좌 : https://www.easyupclass.com

www.youtube.com

 
반응형