0308 - 0314


# 0308 - 0314

# 0308 - pull request

  1. Fork
  2. clone, remote
  3. branch
  4. add, commit, push
  5. pull request
  6. merge pull request


# 0309 - markdown ํ™œ์šฉ - readme.md / git profile

# ๋งˆํฌ๋‹ค์šด ์›น์—๋””ํ„ฐ

  • https://dillinger.io/

# HITS

  • https://hits.seeyoufarm.com/

# Badge

  • https://shields.io/
  • https://simpleicons.org/

# Table Generator

  • https://www.tablesgenerator.com/markdown_tables

# Git-Hub Stats

  • https://github.com/anuraghazra/github-readme-stats

# Emoji

  • https://www.webfx.com/tools/emoji-cheat-sheet/


# 0310 - mockMvc

์‹ค์ œ ๊ฐ์ฒด์™€ ๋น„์Šทํ•˜์ง€๋งŒ ํ…Œ์ŠคํŠธ์— ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋งŒ ๊ฐ€์ง€๋Š” ๊ฐ€์งœ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„์— ๋ฐฐํฌํ•˜์ง€ ์•Š๊ณ ๋„ ์Šคํ”„๋ง MVC ๋™์ž‘์„ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ํด๋ž˜์Šค๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

  1. Controller์— ๋Œ€ํ•œ Testํด๋ž˜์Šค ์ƒ์„ฑ
  2. ํ…Œ์ŠคํŠธ ๋ฉ”์†Œ๋“œ ์‹คํ–‰์ „ ์…‹์—… ๋ฉ”์†Œ๋“œ
	private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext ctx;

	@BeforeEach
	public void setup(){
		this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
	}
    // ํ•„ํ„ฐ ์ถ”๊ฐ€ ๋˜๋Š” alwayDo ์˜ต์…˜ ์ถ”๊ฐ€
    // this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx)
    //             .addFilters(new CharacterEncodingFilter("UTF-8", true))
    //             .alwaysDo(print())
    //             .build();
}
  1. MockMvc ๋ฉ”์†Œ๋“œ ํ™•์ธ
    • .perform()
      • get(),post()
      • header
      • param()
      • accept()
      • contentType()
    • ResultActions
      • andExpert : ์˜ˆ์ƒ๊ฐ’ ๊ฒ€์ฆ
      • andDo : ์š”์ฒญ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ
      • andReturn : ํ…Œ์ŠคํŠธํ•œ ๊ฒฐ๊ณผ ๊ฐ์ฒด๋ฅผ ๋ฐ›์„ ๋•Œ
@Test
    public void getStudyTest() throws Exception {
        mockMvc.perform(get(BASE_URL+"/{seq}", 1L)
                .header("Authorization", "Bearer " + loginedToken)
                .accept(MediaTypes.HAL_JSON_VALUE)
                .contentType(MediaTypes.HAL_JSON_VALUE))
                .andDo(print())
                .andExpect(status().isOk())
                .andDo()
    }


# 0311 - Spring REST Docs

API๋ฌธ์„œ ์ž‘์„ฑ ์ž๋™ํ™” ๋„๊ตฌ
๋Œ€ํ‘œ์ ์œผ๋กœ Swagger, Spring Rest Docs

# Asciidocor

Adoc ํŒŒ์ผ์„ ํ™œ์šฉํ•˜์—ฌ html ๋ฌธ์„œ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋„๊ตฌ

  • ์˜์กด์„ฑ ๋ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ถ”๊ฐ€
<plugin>
                <groupId>org.asciidoctor</groupId>
                <artifactId>asciidoctor-maven-plugin</artifactId>
                <version>1.5.8</version>
                <executions>
                    <execution>
                        <id>generate-docs</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>process-asciidoc</goal>
                        </goals>
                        <configuration>
                            <backend>html</backend>
                            <doctype>book</doctype>
                        </configuration>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.restdocs</groupId>
                        <artifactId>spring-restdocs-asciidoctor</artifactId>
                        <version>${spring-restdocs.version}</version>
                    </dependency>
                </dependencies>
            </plugin>

# ์˜ˆ์ œ

  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
@WebMvcTest(PostController.class)
class PostControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private PostService postService;

    @Test
    public void create() throws Exception {
        final PostResponse post = PostResponse.builder()
                .id(1L)
                .title("first_post")
                .content("hello_my_world")
                .build();
        given(postService.createPost(any())).willReturn(post);

        mvc.perform(post("/posts")
                .accept(MediaType.APPLICATION_JSON)
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"title\":\"first_post\",\"content\":\"hello_my_world\"}"))
                .andExpect(status().isCreated())
                .andExpect(header().stringValues("location", "/posts/" + 1L))
                .andDo(print());
    }
    ...
}
  • Documentation ์ž‘์—…
    • ํ…Œ์ŠคํŠธ ๋ชจ๋“ˆ์•ˆ์— ๋ณ„๋„์˜ ํŒจํ‚ค์ง€ ์ƒ์„ฑํ•˜๊ณ  Documentation์„ ์ž‘( ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— ์ด์–ด ์ž‘์„ฑํ•ด๋„ ๋˜์ง€๋งŒ, ๋”ฐ๋กœ ์ž‘์„ฑํ•˜์—ฌ ๊ด€๋ฆฌ)
public class PostDocumentation {
    public static RestDocumentationResultHandler createPost() {
        return document("posts/create",
                requestFields(
                        fieldWithPath("title").type(JsonFieldType.STRING).description("This is post title."),
                        fieldWithPath("content").type(JsonFieldType.STRING).description("This is post content")
                )
        );
    }
    ...
}
  • mockMvc์— Documentation ์‚ฌ์šฉ ํ•„ํ„ฐ ์ถ”๊ฐ€
    @BeforeEach
    public void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
        mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
                .addFilter(new ShallowEtagHeaderFilter())
                .apply(documentationConfiguration(restDocumentation))
                .build();
    }

    // ๊ฐ ํ…Œ์ŠคํŠธ ๋งˆ์ง€๋ง‰์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด .andDo(PostDocumentation.xx)๋ฅผ ํ˜ธ์ถœํ•ด์ค€๋‹ค.
    @Test
    public void create() throws Exception {
        ...
        mvc.perform(post("/posts")
                .accept(MediaType.APPLICATION_JSON)
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"title\":\"first_post\",\"content\":\"hello_my_world\"}"))
                .andExpect(status().isCreated())
                .andExpect(header().stringValues("location", "/posts/" + 1L))
                .andDo(print())
                .andDo(PostDocumentation.createPost()); // ์ถ”๊ฐ€
    }
  • ๋ฌธ์„œ ์ŠคํŽ™ ์ •์˜๋ฅผ api-guide.adoc ํŒŒ์ผ์„ ์ถ”๊ฐ€
    • src ํด๋” ์•„๋ž˜์— documentation>asccidoc>api-guide.adoc ํŒŒ์ผ ์ถ”๊ฐ€
    • api-guide.adoc ์–‘์‹
  ifndef::snippets[]
:snippets: ../../../build/generated-snippets
endif::[]
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 2
:sectlinks:
:operation-http-request-title: Example Request
:operation-http-response-title: Example Response

[[resources]]
= Resources

[[resources-posts]]
== Post

[[resources-posts-create]]
=== ํฌ์ŠคํŠธ ์ถ”๊ฐ€
operation::posts/create[snippets='http-request,http-response,request-fields,request-body']

[[resources-posts-getAll]]
=== ํฌ์ŠคํŠธ ์ „์ฒด ์กฐํšŒ
operation::posts/getAll[snippets='http-request,http-response,response-body']

[[resources-posts-get]]
=== ํฌ์ŠคํŠธ ์กฐํšŒ
operation::posts/get[snippets='http-request,http-response,response-body']

[[resources-posts-update]]
=== ํฌ์ŠคํŠธ ์ˆ˜์ •
operation::posts/update[snippets='http-request,http-response,request-fields,request-body']

[[resources-posts-delete]]
=== ํฌ์ŠคํŠธ ์‚ญ์ œ
operation::posts/delete[snippets='http-request,http-response']
  • build test

# 0312 - Spring HATEOAS

Hypermedia As The Engine Of Application State
RESTful API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ „์ ์œผ๋กœ ์„œ๋ฒ„์™€ ๋™์ ์ธ ์ƒํ˜ธ์ž‘์šฉ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ
ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์–ด๋– ํ•œ ์š”์ฒญ์„ ํ•  ๋•Œ, ์š”์ฒญ์— ํ•„์š”ํ•œ(์˜์กด๋˜๋Š”) URI๋ฅผ ์‘๋‹ต์— ํฌํ•จ์‹œ์ผœ ๋ฐ˜ํ™˜

# Content Type

  • ContentType์„ application/hal+json์œผ๋กœ ์ „๋‹ฌํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” _links ํ•„๋“œ์— ๋งํฌ ์ •๋ณด๊ฐ€ ์žˆ๋‹ค๊ณ  ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

# ์˜ˆ์ œ

@PostMapping("/employees")
ResponseEntity<?> newEmployee(@RequestBody Employee newEmployee) {

  EntityModel<Employee> entityModel = assembler.toModel(repository.save(newEmployee));

  return ResponseEntity //
      .created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()) //
      .body(entityModel);
}

https://spring.io/guides/tutorials/rest/


# 0313 - Reverse Proxy

์ปดํ“จํ„ฐ ๋„คํŠธ์›Œํฌ์—์„œ ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ๋ž€ ํ•˜๋‚˜ ์ด์ƒ์˜ ์„œ๋ฒ„๋กœ ๋ถ€ํ„ฐ ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋Œ€์‹ ํ•ด์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋Š” ํ”„๋ก์‹œ ์„œ๋ฒ„, ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ ‘์ด‰ํ•˜๋Š”๊ฒƒ์„ ์ค‘๊ฐœ

  1. Load Balancing
  2. Web acceleration
  3. Security and anonymity

# 0314 - API ๋ช…์„ธ์„œ ์ž๋™ ์ƒ์„ฑ

Spring Rest Docs

  1. ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ž‘์„ฑ
  2. ํ…Œ์ŠคํŠธ ์‹คํ–‰
  3. ์ƒ์„ฑ๋œ snippets ๋ฌธ์„œ ํ™•์ธ
curl-request.adoc : ํ˜ธ์ถœ์— ๋Œ€ํ•œ curl ๋ช…๋ น์„ ํฌํ•จ ํ•˜๋Š” ๋ฌธ์„œ
httpie-request.adoc : ํ˜ธ์ถœ์— ๋Œ€ํ•œ http ๋ช…๋ น์„ ํฌํ•จ ํ•˜๋Š” ๋ฌธ์„œ
http-request.adoc : http ์š”์ฒญ ์ •๋ณด ๋ฌธ์„œ
http-response.adoc : http ์‘๋‹ต ์ •๋ณด ๋ฌธ์„œ
request-body.adoc : ์ „์†ก๋œ http ์š”์ฒญ ๋ณธ๋ฌธ ๋ฌธ์„œ
response-body.adoc : ๋ฐ˜ํ™˜๋œ http ์‘๋‹ต ๋ณธ๋ฌธ ๋ฌธ์„œ
request-parameters.adoc : ํ˜ธ์ถœ์— parameter ์— ๋Œ€ํ•œ ๋ฌธ์„œ
path-parameters.adoc : http ์š”์ฒญ์‹œ url ์— ํฌํ•จ๋˜๋Š” path parameter ์— ๋Œ€ํ•œ ๋ฌธ์„œ
request-fields.adoc : http ์š”์ฒญ object ์— ๋Œ€ํ•œ ๋ฌธ์„œ
response-fields.adoc : http ์‘๋‹ต object ์— ๋Œ€ํ•œ ๋ฌธ์„œ
  1. API ๋ช…์„ธ์„œ ์ž‘์„ฑ
  • ์Šค๋‹ˆํŽซ ๋ฌธ์„œ ๊ฒฝ๋กœ ์ง€์ •
ifndef::snippets[]
:snippets: ../../../target/generated-snippets
endif::[]
  • ์›ํ•˜๋Š” ์Šค๋‹ˆํŽซ ์ถ”๊ฐ€
include::{snippets}/user-controller-test/get-list/curl-request.adoc[]
  1. maven install
  • html๋กœ ๋ฐ˜ํ™˜ / api ๋ช…์„ธ์„œ ๋ณต์‚ฌ ์„ค์ •
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    ...
    <build>
        <plugins>
            ...
            <!-- 1. API ๋ช…์„ธ์„œ๋ฅผ html๋กœ ๋ณ€ํ™˜ -->
            <plugin>
                <groupId>org.asciidoctor</groupId>
                <artifactId>asciidoctor-maven-plugin</artifactId>
                <version>1.5.3</version>
                <executions>
                    <execution>
                        <id>generate-docs</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>process-asciidoc</goal>
                        </goals>
                        <configuration>
                            <backend>html</backend>
                            <doctype>book</doctype>
                        </configuration>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.restdocs</groupId>
                        <artifactId>spring-restdocs-asciidoctor</artifactId>
                        <version>2.0.2.RELEASE</version>
                    </dependency>
                </dependencies>
            </plugin>

            <!-- 2. html ๋กœ ๋ณ€ํ™˜๋œ API ๋ช…์„ธ์„œ๋ฅผ static ์˜์—ญ์œผ๋กœ ๋ณต์‚ฌ -->
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.0.1</version>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>
                                ${project.build.outputDirectory}/static/docs
                            </outputDirectory>
                            <resources>
                                <resource>
                                    <directory>
                                        ${project.build.directory}/generated-docs
                                    </directory>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            ...
        </plugins>
    </build>
    ...
</project>
  1. html ๋ฌธ์„œ ํ™•์ธ
Last update: September 13, 2022 21:44
Contributors: ahnjs , jaesungahn91