DevKim

[์Šคํ”„๋ง with AWS] mustache๋กœ ํ™”๋ฉด ๊ตฌ์„ฑํ•˜๊ธฐ ๋ณธ๋ฌธ

Spring Boot

[์Šคํ”„๋ง with AWS] mustache๋กœ ํ™”๋ฉด ๊ตฌ์„ฑํ•˜๊ธฐ

on_doing 2021. 7. 11. 17:02
728x90

๐ŸƒCH.04 ๋จธ์Šคํ…Œ์น˜๋กœ ํ™”๋ฉด ๊ตฌ์„ฑํ•˜๊ธฐ๐Ÿƒ

 

์ด ์ฑ…์—์„œ ๋จธ์Šคํ…Œ์น˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค๊ณ ํ•œ๋‹ค.

1. ๋ฌธ๋ฒ•์ด ๋‹ค๋ฅธ ํ…œํ”Œ๋ฆฟ ์—”์ง„๋ณด๋‹ค ๋‹จ์ˆœํ•˜๋ฉฐ

2. ๋กœ์ง ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์–ด, View์˜ ์—ญํ• ๊ณผ ์„œ๋ฒ„์˜ ์—ญํ• ์ด ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ๋  ์ˆ˜ ์žˆ๊ณ 

3. .js / .java๋ฅผ 2๊ฐ€์ง€๊ฐ€ ๋‹ค ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‚˜์˜ ๋ฌธ๋ฒ•์œผ๋กœ ํด๋ผ์ด์–ธํŠธ/์„œ๋ฒ„ ํ…œํ”Œ๋ฆฟ์„ ๋ชจ๋‘ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค

 

๋จธ์Šคํ…Œ์น˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , build.gradle์— ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 


๊ฐ„๋‹จํ•œ ๊ธฐ๋ณธ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค๊ณ  ๋จธ์Šคํ…Œ์น˜์™€ API๊ฐ€ ์ž˜ ์ž‘๋™์ด ๋˜๋Š”์ง€ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณด์ž.

 

- index.mustache

<!DOCTYPE HTML>
<html>
<head>
    <title>์Šคํ”„๋ง ๋ถ€ํŠธ ์›น์„œ๋น„์Šค</title>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
</head>

<body>
<h1> ์Šคํ”„๋ง ๋ถ€ํŠธ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์›น ์„œ๋น„์Šค</h1>
</body>
</html>

 

-IndexController ํ…Œ์ŠคํŠธ

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IndexControllerTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void ๋ฉ”์ธํŽ˜์ด์ง€_๋กœ๋”ฉ() throws Exception{

        //when
        String body = this.restTemplate.getForObject("/", String.class);

        //then
        assertThat(body).contains("์Šคํ”„๋ง ๋ถ€ํŠธ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์›น ์„œ๋น„์Šค");
        
    }
}

์‹ค์ œ๋กœ URL ํ˜ธ์ถœ ์‹œ ํŽ˜์ด์ง€์˜ ๋‚ด์šฉ์ด ์ œ๋Œ€๋กœ ํ˜ธ์ถœ๋˜๋Š”์ง€ ,์ฆ‰ ํ•ด๋‹น ๋ฌธ์ž์—ด์ด ํฌํ•จ๋˜๋Š”์ง€ ํ…Œ์Šค๋ฅผ ํ•ด๋ณด์•˜๋‹ค.

 

๋ถ€ํŠธ์ŠคํŠธ๋žฉ,์ œ์ด์ฟผ๋ฆฌ ๋“ฑ ํ”„๋ก ํŠธ์—”๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์™ธ๋ถ€ CDN์„ ์‚ฌ์šฉํ–ˆ๊ณ ,

๋ ˆ์ด์•„์›ƒ ๋ฐฉ์‹(๊ณตํ†ต ์˜์—ญ์„ ๋ณ„๋„์˜ ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ํ•„์š”ํ•œ ๊ณณ์—์„œ ๊ฐ€์ ธ๋‹ค ์“ฐ๋Š” ๋ฐฉ์‹)์œผ๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค.

ํŽ˜์ด์ง€์˜ ๋กœ๋”ฉ ์†๋„๋ฅผ ๋†’์ด๊ธฐ ์œ„ํ•ด css๋Š” header์—, js๋Š” footer์— ๋‘”๋‹ค.


๐Ÿ“Œ ๊ฒŒ์‹œ๊ธ€ ๋“ฑ๋ก 

 

[ ๊ธ€ ๋“ฑ๋ก ๋ฒ„ํŠผ ]

{{>layout/header}}

<h1>์Šคํ”„๋ง๋ถ€ํŠธ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์›น ์„œ๋น„์Šค Ver.2</h1>
<div class="col-md-12">
    <div class="row">
        <div class="col-md-6">
            <a href="/posts/save" role="button" class="btn btn-primary">๊ธ€ ๋“ฑ๋ก</a>

        </div>
    </div>
    <br>
    <!-- ๋ชฉ๋ก ์ถœ๋ ฅ ์˜์—ญ -->
    <table class="table table-horizontal table-bordered">
        <thead class="thead-strong">
        <tr>
            <th>๊ฒŒ์‹œ๊ธ€๋ฒˆํ˜ธ</th>
            <th>์ œ๋ชฉ</th>
            <th>์ž‘์„ฑ์ž</th>
            <th>์ตœ์ข…์ˆ˜์ •์ผ</th>
        </tr>
        </thead>
        <tbody id="tbody">
        {{#posts}}
            <tr>
                <td>{{id}}</td>
                <td><a href="/posts/update/{{id}}">{{title}}</a></td>
                <td>{{author}}</td>
                <td>{{modifiedDate}}</td>
            </tr>
        {{/posts}}
        </tbody>
    </table>
</div>

{{>layout/footer}}

{{>}}

: ํ˜„์žฌ ๋จธ์Šคํ…Œ์น˜ ํŒŒ์ผ์„ ๊ธฐ์ค€์œผ๋กœ ๋‹ค๋ฅธ ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜จ๋‹ค.

 

{{#posts}} ~ {{/posts}}

: posts๋ผ๋Š” List๋ฅผ ์ˆœํšŒํ•œ๋‹ค.

 

{{๋ณ€์ˆ˜๋ช…}}

:List์—์„œ ๋ฝ‘์•„๋‚ธ ๊ฐ์ฒด์˜ ํ•„๋“œ

 

[ ๊ธ€ ๋“ฑ๋กํ•˜๋Š” /posts/save ํŽ˜์ด์ง€๋กœ ์ด๋™ ]

@GetMapping("/posts/save")
public String postsSave()
{
	return "posts-save";
}

[ ๊ธ€ ๋“ฑ๋ก ํŽ˜์ด์ง€ ]

{{>layout/header}}

<h1>๊ฒŒ์‹œ๊ธ€ ๋“ฑ๋ก</h1>

<div class="col-md-12">
    <div class="col-md-4">
        <form>
            <div class="form-group">
                <label for="title">์ œ๋ชฉ</label>
                <input type="text" class="form-control" id="title" placeholder="์ œ๋ชฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”">
            </div>
            <div class="form-group">
                <label for="author"> ์ž‘์„ฑ์ž </label>
                <input type="text" class="form-control" id="author" placeholder="์ž‘์„ฑ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”">
            </div>
            <div class="form-group">
                <label for="content"> ๋‚ด์šฉ </label>
                <textarea class="form-control" id="content" placeholder="๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”"></textarea>
            </div>
        </form>
        <a href="/" role="button" class="btn btn-secondary">์ทจ์†Œ</a>
        <button type="button" class="btn btn-primary" id="btn-save">๋“ฑ๋ก</button>
    </div>
</div>

{{>layout/footer}}

๐Ÿ“Œ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ 

 

[ Repository์— Query ์ถ”๊ฐ€ ]

๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜์—ฌ ์กฐํšŒ๊ธฐ๋Šฅ ์ถ”๊ฐ€

...
@Query("select p from Posts p order by p.id DESC")
List<Posts> findAllDesc();

[ ๋‹ด์•„์„œ ๋ณด๋‚ผ DTO ์ƒ์„ฑ ]

@Getter
@NoArgsConstructor
public class postsListResponseDto {

    private Long id;
    private String title;
    private String content;
    private String author;

    public postsListResponseDto(Posts posts)
    {
        this.id=posts.getId();
        this.title=posts.getTitle();
        this.content= posts.getContent();
        this.author= posts.getAuthor();
    }
}

 

[ Service์— ์ „์ฒด์กฐํšŒ ์ถ”๊ฐ€ ]

foreach๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋žŒ๋‹ค์‹์„ ์ ์šฉํ–ˆ๋‹ค.

public List<postsListResponseDto> findAllDesc()
{
	return postsRepository.findAllDesc().stream()
			.map(postsListResponseDto::new)
			.collect(Collectors.toList());
}

 

[ Controller ์ˆ˜์ • ]

model์— ๋‹ด์•„์„œ List๋ฅผ index.mustache์— ์ „๋‹ฌํ•œ๋‹ค.

@GetMapping("/")
public String index(Model model)
{
	List<Posts> postsList = postsRepository.findAllDesc();
	model.addAttribute("posts",postsList);
	return "index";
}

๐Ÿ“Œ ๊ฒŒ์‹œ๊ธ€ ์ˆ˜์ •

 

[ ์ˆ˜์ •ํ™”๋ฉด๊ณผ ์—ฐ๊ฒฐํ•  Controller ]

์ˆ˜์ •ํ™”๋ฉด์— ์ˆ˜์ •๋œ Post๋ฅผ Dto๋กœ ๋ณ€ํ™˜ -> model์— ๋‹ด์•„ ์ „๋‹ฌ

    @GetMapping("posts/update/{id}")
    public String postsUpdate(@PathVariable Long id, Model model)
    {
        Posts posts = postsRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("ํ•ด๋‹นํ•˜๋Š” ์•„์ด๋””๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
        );
        PostsResponseDto postsResponseDto = new PostsResponseDto(posts);

        model.addAttribute("post",postsResponseDto);
        return "posts-update";
    }

 

[ js์— update ์ถ”๊ฐ€ ]

update : function () {
        var data = {
            title: $('#title').val(),
            content: $('#content').val()
        };

        var id = $('#id').val();

        $.ajax({
            type: 'PUT',
            url: '/api/v1/posts/'+id,
            dataType: 'json',
            contentType:'application/json; charset=utf-8',
            data: JSON.stringify(data)
        }).done(function() {
            alert('๊ธ€์ด ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
            window.location.href = '/';
        }).fail(function (error) {
            alert(JSON.stringify(error));
        });
    }
728x90
Comments