오늘은 글을 쓰기 위한 markdown과 catetory 화면은 만들어보자. markdown을 만들기 전에 catetory 부터 한번 보자.
서버쪽은 다 만들었으니 view만 만들면 될 것같다.
templates/category 라는 폴더를 만들고 list.html 파일을 만들t자. 그리고 아래와 같이 html코드를 넣자.

<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorator="layouts/main">
... //기타 

<div class="container" layout:fragment="content">
    <li class="next">
        <a th:href="@{/categories/new}">write</a>
    </li>
    <table class="table table-striped">
        <thead>
        <tr>
            <th>#</th>
            <th>name</th>
            <th>date</th>
        </tr>
        </thead>
        <tbody>
        <tr th:each="category,index : ${categories.content}">
            <th scope="row" th:text="${index.count}"></th>
            <td><a th:text="${category.name}" th:href="@{'/categories/' + ${category.id} + '/edit'}"></a></td>
            <td th:text="${#temporals.format(category.regDate, 'yyyy-MM-dd')}"></td>
        </tr>
        </tbody>
    </table>
</div>

카테고리를 등록할 수 있는 페이지로 이동하는 버튼이 상단에 있다. 우리가 일반적으로 JSP를 할때 하던 그런 코드와 비슷하다. 카테고리명을 클릭하면 상세 페이지로 이동할 수 있다. 상세 페이지에서는 변경 또는 삭제가 가능하게 만들어야 된다.
일단 새로 등록 할 수 있는 페이지를 만들어 보자.

<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorator="layouts/main">

...//기타

<div class="container" layout:fragment="content">
    <form class="form-horizontal well bs-component col-lg-10 col-lg-offset-1" th:action="@{/categories}"
          th:object="${categoryDto}" method="post" th:id="category">
        <fieldset>
            <div class="form-group" th:classappend="(${#fields.hasErrors('name')}? ' has-error')">
                <label for="name" class="col-lg-2 control-label">카테고리 명</label>
                <div class="col-lg-10">
                    <input type="text" class="form-control" id="name" name="name" th:field="*{name}"
                           placeholder="Title"/>
                    <span class="help-block" th:if="${#fields.hasErrors('name')}"
                          th:errors="*{name}"></span>
                </div>
            </div>
            <div class="form-group">
                <div class="col-lg-10 col-lg-offset-2">
                    <button type="submit" class="btn btn-primary">Submit</button>
                </div>
            </div>
        </fieldset>
    </form>
</div>

위의 코드는 새로 등록 할 수 있는 페이지다. 일단 필자는 등록 페이지 수정페이지 각각 한개씩 만들었다. 물론 등록, 수정 페이지를 한개로 만들 수도 있을 텐데.. 글쎄 뭐가 좋은지는.. 개발자들의 몫이 아닐까 생각된다. 그때 그때 잘 판단해서 하면 될 듯 하다.
여기서는 파라미터가 잘 못 되었을때 에러 메시지를 출력해주는 그런 코드들도 들어있다. th:classappend="(${#fields.hasErrors('name')}? ' has-error')" name이라는 필드에 에러가 발생하면 has-error라는 class를 넣는 코드이다. 그리고 th:if="${#fields.hasErrors('name')}" th:errors="*{name} 이것 또한 마찬가지로 name이라는 필드에 에러가 있다면 메시지를 출력해주는 그런 코드이다. 한번 테스트를 해보자. 서버를 시작 후에 http://localhost:8080/categories/new 로 접속해보자.

5.blog2

위의 화면은 등록할 때의 페이지다. 뭐 나쁘지않다. 화면도.. 다음은 아무것도 입력하지 않았을때의 화면이다.

5.blog3

다음은 등록을 하고 다음의 모습이다.

5.blog4

아주 깔끔하다. 다음으로는 수정 페이지를 만들어보자. 수정페이지도 등록페이지랑 거의 비슷하다.

<div class="container" layout:fragment="content">
    <form class="form-horizontal well bs-component col-lg-10 col-lg-offset-1" th:action="@{'/categories/'+${id}+'/edit'}"
          th:object="${categoryDto}" method="post" th:id="category">
        <fieldset>
            <div class="form-group" th:classappend="(${#fields.hasErrors('name')}? ' has-error')">
                <label for="name" class="col-lg-2 control-label">카테고리 명</label>
                <div class="col-lg-10">
                    <input type="text" class="form-control" id="name" name="name" th:field="*{name}"
                           placeholder="Title"/>
                    <span class="help-block" th:if="${#fields.hasErrors('name')}"
                          th:errors="*{name}"></span>
                </div>
            </div>
            <div class="form-group">
                <div class="col-lg-10 col-lg-offset-2">
                    <button type="submit" class="btn btn-primary">Submit</button>
                    <input class="btn btn-danger" type="button" value="Delete" th:onclick="'deleteCategory(\'' + ${categoryDto.id} + '\')'"/>
                </div>
            </div>
        </fieldset>
    </form>
    <script type="text/javascript" th:inline="javascript">
        function deleteCategory(categoryId){
            document.getElementById("category").action = "/categories/"+categoryId +"/delete";
            document.getElementById("category").submit();
        }
    </script>
</div>

만들기 나름이겠지만 필자는 위와 같이 만들었다. thymeleaf를 처음 하다보니 모르는게 많다. javascript를 만들고 보니 안된다. 실제 브라우저를 띄워서 개발자 모드로 봐도 javascript가 없다. 그래서 div 안쪽으로 넣었더니 된다. 이게 맞는건지 잘 모르겠으나 일단 되니 그냥 넘어자가. 아마 layout과 관련이 있는거 같다. 저걸로 30분동안 헤멘듯하다. 기존 등록 페이지와 거의 비슷하니 설명은 생략하자. 한번씩 등록하고 수정하고 삭제를 해보자.

이제 카테고리가 완료 되었으니 markdown 에디터를 만들어보자.
여기에 보면 자바스크립트로 누군가 괜찮은 markdown 에디터를 만들어 놨다. 일단 다운 받아서 static 폴더 아래 원하는 부분에 넣자. 필자의 경우에는 아래와 같이 넣었다.
5.blog1

사용하지 않는 파일들은 삭제 했고 폴더 구조도 변경 살짝 변경했다. 그리고 원래는 highlightjs 테마에는 default.css만 있어도 된다. highlightjs 테마들이 여러개 있는데 필자는 그 중에서 github-gist를 선택했다. 그게 제일 이쁜거 같다. 여기 블로그도 gist 테마이다. 다른 테마도 많으니 원하는 테마를 선택해서 넣으면 된다. highlightjs 를 참고 하면 되겠다.

다운받은 markdown 라이브러리에 index.html을 보면 자바스크립트와 css가 길게 있다. 필자는 정신없어서 딴곳으로 뺏다. post.js 와 post.css를 만들어서 정적파일이 모인 static아래 두었다.

    <script th:src="@{/markdown/js/markdown-it.js}"></script>
    <script th:src="@{/markdown/js/markdown-it-footnote.js}"></script>
    <script th:src="@{/markdown/js/highlight.pack.js}"></script>
    <script th:src="@{/codemirror/lib/codemirror.js}"></script>
    <script th:src="@{/codemirror/overlay.js}"></script>
    <script th:src="@{/codemirror/markdown/markdown.js}"></script>
    <script th:src="@{/codemirror/gfm/gfm.js}"></script>
    <script th:src="@{/markdown/js/rawinflate.js}"></script>
    <script th:src="@{/markdown/js/rawdeflate.js}"></script>
    <link rel="stylesheet" th:href="@{/codemirror/css/base16-light.css}"/>
    <link rel="stylesheet" th:href="@{/codemirror/lib/codemirror.css}"/>
    <link rel="stylesheet" th:href="@{/markdown/css/github-gist.css}" />
    <link rel="stylesheet" th:href="@{/css/post.css}" />
    <link th:href="@{/vendor/bootstrap/css/bootstrap.min.css}" rel="stylesheet"/>

스크립트와 css가 많다. 위와 같이 임포트 해주자. 만약 markdown의 테마를 바꾸고 싶다면 github-gist.css 이곳만 변경해주면 된다.

..// 다른 기타 기능

<div id="in">
    <input type="hidden" id="content" name="content" th:field="*{content}"/>
    <input type="hidden" id="code" th:field="*{code}" />
</div>
<div id="out" >
</div>

..// 다른 기타 기능
<div id="menu">
    <input class="btn btn-primary" type="button" value="Submit" onclick="saveAsHtml()"/>
</div>

나머지는 카테고리와 비슷한 코드이므로 소스로 확인하고 여기서 중요한 부분은 in과 out이다. in은 실제 포스팅할 부분이고 out부분은 글을 쓰면 바로바로 마크다운을 확인 할 수 있는 부분이다. 여기서 content와 code 두개가 있는데 code는 원본 code이고 content 경우에는 code를 html 코드로 바꾸어서 들어가는 코드이다. 뭐 물론 content를 서버에서 markdown의 html 코드로 변환시킬수도 있는데 굳이 변환된 코드가 있는데 서버에서 할 필요가 없을 거 같아서 같이 넘겼다.

다운받은 라이브러리에서는 markdown을 다운로드 받을 수 있다. 헌데 우리는 다운받을 필요는 없고 코드만 저장하면 된다.

function saveAsHtml() {
    document.getElementById('content').value = document.getElementById('out').innerHTML;
    document.getElementById('post').submit();
}

위와 같이 out에 있는 코드를 content에 넣어 서버로 주면 된다. 얼추 마크 다운도 완성되었다. 어차피 등록과 수정 페이지는 비슷비슷하니 소스를 참고하면 되겠다.
포스트를 등록할 때 카테고리를 선택해야 되므로 등록이나 수정 페이지로 이동할 때 카테고리 리스트도 가져와야 한다. Post쪽에 다음과 같이 추가하자. 매번 가져 올 필요 없다면 그때 그때 마다 가져와도 되긴하나 일단 필자는 매번 가져오는 방식으로 했다.

@ModelAttribute("categories")
public List<Category> categories(){
  return categoryService.findAll();
}

마크다운쪽은 소스가 길기 때문에 다 넣지 못했다. github에 올려두었으니 참고하면 되겠다.
일단 다 만들었으니 서버를 띄워 http://localhost:8080/posts/new 에 접속해보자.
5.blog5

뭐 그럭저럭 괜찮은 UI가 나왔다. 에디터는 헤더와 푸터를 없앴다. 있어도 안들어가고 화면도 작고 그래서 뺏다. UI 작업은 잘 하지도 못하고 그래서 있는 그대로 넣었다. 거의 대부분 UI는 변경하지 않았다. 위치 정보만 바꾸었고 나머지는 그대로 작업했다.
아래와 같이 markdown을 넣어보자.

spring Boot blog
-----

1. [spring-boot] 블로그를 만들자 (1)
2. [spring-boot] 블로그를 만들자. (2) JPA
3. [spring-boot] 블로그를 만들자. (3) Category 와 Comment
4. [spring-boot] 블로그를 만들자. (4) thymeleaf
5. [spring-boot] 블로그를 만들자. (5) markdown, catetory

### java
```java
class Main {
  public static void main(String[] args){
    System.out.println("Spring Boot Blog");
  }
}
```

### xml
```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
```

### js
```js
function hello(){
    console.log("hello world");
}
```
### css

```css
sub, sup {
    font-size: 75%;
    line-height: 0;
    position: relative;
    vertical-align: baseline;
}
```

5.blog6

나쁘진 않지만 완전 마음에는 들지 않는다. 하지만 이걸 만들기 위해 엄청나게 고생했다. 아무튼 저장을 해보자. 그럼 아래와 같은 화면이 출력 될 것이다.
5.blog7

여기에는 테마가 적용 되지 않았다. 여기 역시 테마를 적용 시키려면 css를 추가해야 된다. post.html에 css를 추가 시키자.

<link rel="stylesheet" th:href="@{/markdown/css/github-gist.css}" />

그리고 다시 확인해보면 테마가 추가된 화면을 볼 수 있다.
5.blog8

이제 위와 같이 그럭저럭 나쁘지 않은 UI를 볼 수 있다. 블로그를 작성하는 부분이 나와서 그래도 어느정도 나온거 같다.
흠 다음 시간에는 댓글과 캐시? 아니면 github sso 중 하나를 선택해서 해야겠다.
현재까지의 소스는 여기에 올라가 있다.

  1. [spring-boot] 블로그를 만들자 (1)

  2. [spring-boot] 블로그를 만들자. (2) JPA

  3. [spring-boot] 블로그를 만들자. (3) Category 와 Comment

  4. [spring-boot] 블로그를 만들자. (4) thymeleaf

  5. [spring-boot] 블로그를 만들자. (5) markdown, catetory

  6. [spring-boot] 블로그를 만들자. (6) 댓글과 Navigation

  7. [spring-boot] 블로그를 만들자. (7) 캐시와 에러페이지

  8. [spring-boot] 블로그를 만들자. (8) GitHub login

  9. [spring-boot] 블로그를 만들자. (9) CI 와 배포