ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] REST API 만들기(11) - JSON Root Element 추가
    Spring Boot/2.4.x - REST API 만들기 2020. 5. 20. 16:07
    반응형

    JSON과 XML 두 방식을 모두 사용하는 경우 기본적으로 JSON은 Root Element룰 사용하지 않아 데이터 형식이 다른 경우가 있습니다. 게시글 목록 조회 시에 JSON으로 응답받는 경우 Root Element가 적용되지 않은 것을 확인할 수 있습니다.

     

    게시글 목록 조회 시(http://localhost:8080/board) JSON, XML 응답

    JSON

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    {
        "boards": [
            {
                "board_seq": 1,
                "board_re_ref": 0,
                "board_re_lev": 0,
                "board_re_seq": 0,
                "board_writer": "게시글 작성자1",
                "board_subject": "게시글 제목1",
                "board_content": "게시글 내용1",
                "board_hits": 0,
                "del_yn": "N",
                "ins_user_id": "TEST",
                "ins_date": "2020-05-19 18:30:24",
                "upd_user_id": null,
                "upd_date": null
            },
            {
                "board_seq": 2,
                "board_re_ref": 0,
                "board_re_lev": 0,
                "board_re_seq": 0,
                "board_writer": "게시글 작성자2",
                "board_subject": "게시글 제목2",
                "board_content": "게시글 내용2",
                "board_hits": 0,
                "del_yn": "N",
                "ins_user_id": "TEST",
                "ins_date": "2020-05-19 18:30:24",
                "upd_user_id": null,
                "upd_date": null
            },
            {
                "board_seq": 3,
                "board_re_ref": 0,
                "board_re_lev": 0,
                "board_re_seq": 0,
                "board_writer": "게시글 작성자3",
                "board_subject": "게시글 제목3",
                "board_content": "게시글 내용3",
                "board_hits": 0,
                "del_yn": "N",
                "ins_user_id": "TEST",
                "ins_date": "2020-05-19 18:30:24",
                "upd_user_id": null,
                "upd_date": null
            },
            {
                "board_seq": 4,
                "board_re_ref": 0,
                "board_re_lev": 0,
                "board_re_seq": 0,
                "board_writer": "게시글 작성자4",
                "board_subject": "게시글 제목4",
                "board_content": "게시글 내용4",
                "board_hits": 0,
                "del_yn": "N",
                "ins_user_id": "TEST",
                "ins_date": "2020-05-19 18:30:24",
                "upd_user_id": null,
                "upd_date": null
            },
            {
                "board_seq": 5,
                "board_re_ref": 0,
                "board_re_lev": 0,
                "board_re_seq": 0,
                "board_writer": "게시글 작성자5",
                "board_subject": "게시글 제목5",
                "board_content": "게시글 내용5",
                "board_hits": 0,
                "del_yn": "N",
                "ins_user_id": "TEST",
                "ins_date": "2020-05-19 18:30:24",
                "upd_user_id": null,
                "upd_date": null
            }
        ]
    }
    cs

    XML

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <boards>
        <board>
            <board_seq>1</board_seq>
            <board_re_ref>0</board_re_ref>
            <board_re_lev>0</board_re_lev>
            <board_re_seq>0</board_re_seq>
            <board_writer>게시글 작성자1</board_writer>
            <board_subject>게시글 제목1</board_subject>
            <board_content>게시글 내용1</board_content>
            <board_hits>0</board_hits>
            <del_yn>N</del_yn>
            <ins_user_id>TEST</ins_user_id>
            <ins_date>2020-05-19 18:30:24</ins_date>
        </board>
        <board>
            <board_seq>2</board_seq>
            <board_re_ref>0</board_re_ref>
            <board_re_lev>0</board_re_lev>
            <board_re_seq>0</board_re_seq>
            <board_writer>게시글 작성자2</board_writer>
            <board_subject>게시글 제목2</board_subject>
            <board_content>게시글 내용2</board_content>
            <board_hits>0</board_hits>
            <del_yn>N</del_yn>
            <ins_user_id>TEST</ins_user_id>
            <ins_date>2020-05-19 18:30:24</ins_date>
        </board>
        <board>
            <board_seq>3</board_seq>
            <board_re_ref>0</board_re_ref>
            <board_re_lev>0</board_re_lev>
            <board_re_seq>0</board_re_seq>
            <board_writer>게시글 작성자3</board_writer>
            <board_subject>게시글 제목3</board_subject>
            <board_content>게시글 내용3</board_content>
            <board_hits>0</board_hits>
            <del_yn>N</del_yn>
            <ins_user_id>TEST</ins_user_id>
            <ins_date>2020-05-19 18:30:24</ins_date>
        </board>
        <board>
            <board_seq>4</board_seq>
            <board_re_ref>0</board_re_ref>
            <board_re_lev>0</board_re_lev>
            <board_re_seq>0</board_re_seq>
            <board_writer>게시글 작성자4</board_writer>
            <board_subject>게시글 제목4</board_subject>
            <board_content>게시글 내용4</board_content>
            <board_hits>0</board_hits>
            <del_yn>N</del_yn>
            <ins_user_id>TEST</ins_user_id>
            <ins_date>2020-05-19 18:30:24</ins_date>
        </board>
        <board>
            <board_seq>5</board_seq>
            <board_re_ref>0</board_re_ref>
            <board_re_lev>0</board_re_lev>
            <board_re_seq>0</board_re_seq>
            <board_writer>게시글 작성자5</board_writer>
            <board_subject>게시글 제목5</board_subject>
            <board_content>게시글 내용5</board_content>
            <board_hits>0</board_hits>
            <del_yn>N</del_yn>
            <ins_user_id>TEST</ins_user_id>
            <ins_date>2020-05-19 18:30:24</ins_date>
        </board>
    </boards>
    cs

     

    JSON Root Element 추가

    1. pom.xml 의존성 추가

    pom.xml에 Jackson 어노테이션 대안으로 JAXB 어노테이션을 사용할수 있도록 지원하는 jackson-module-jaxb-annotations를 추가하세요.

    pom.xml

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    <?xml version="1.0" encoding="UTF-8"?>
    <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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.4.8</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.api</groupId>
        <artifactId>board</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>board</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
     
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-configuration-processor -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.4</version>
            </dependency>
             
            <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/org.bgee.log4jdbc-log4j2/log4jdbc-log4j2-jdbc4.1 -->
            <dependency>
                <groupId>org.bgee.log4jdbc-log4j2</groupId>
                <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
                <version>1.16</version>
            </dependency>
            
            <!-- JUnit4 사용하기 위해서 Vintage Engine 모듈을 제외 -->
            <dependency>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.hamcrest</groupId>
                        <artifactId>hamcrest-core</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/org.springframework/spring-oxm -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-oxm</artifactId>
            </dependency>
            
            <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-jaxb-annotations -->
            <dependency>
                <groupId>com.fasterxml.jackson.module</groupId>
                <artifactId>jackson-module-jaxb-annotations</artifactId>
            </dependency>
            
        </dependencies>
     
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
     
    </project>
    cs

     

    2. WebMvcConfig.java 수정

    application/json 형태로 응답 시 @XmlRootName 애노테이션을 적용하려면, ObjectMapper에 DeserializationFeature.UNWRAP_ROOT_VALUE, SerializationFeature.WRAP_ROOT_VALUE 옵션을 추가한 후 JaxbAnnotationModule을 모듈로 등록하세요.

    WebMvcConfig.java

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    package com.api.board.config;
     
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.ComponentScan.Filter;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
    import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
    import org.springframework.oxm.jaxb.Jaxb2Marshaller;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
     
    import com.api.board.interceptor.BoardInterceptor;
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
     
    @ComponentScan(basePackages = {"com.api.board.controller"}, useDefaultFilters = false, includeFilters = {@Filter(Controller.class)})
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
     
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new BoardInterceptor())
                    .addPathPatterns("/**")
                    .excludePathPatterns("/sample/**");
        }
        
        @Bean
        public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {
     
            MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
            
            ObjectMapper objectMapper = converter.getObjectMapper();
            objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
            objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
     
            JaxbAnnotationModule module = new JaxbAnnotationModule();
            objectMapper.registerModule(module);
     
            return converter;
        }
        
        @Bean
        public MarshallingHttpMessageConverter marshallingHttpMessageConverter() {
            MarshallingHttpMessageConverter converter = new MarshallingHttpMessageConverter();
            converter.setMarshaller(jaxb2Marshaller());
            converter.setUnmarshaller(jaxb2Marshaller());
            return converter;
        }
        
        @Bean
        public Jaxb2Marshaller jaxb2Marshaller() {
            Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
            marshaller.setPackagesToScan(new String[] { "com.api.board.domain" });
            return marshaller;
        }
    }
    cs

     

    3. JSON Root Element 확인

    게시글 목록 조회 시에 JSON으로 응답받는 경우 Root Element가 적용된 것을 확인할 수 있습니다.

     

    게시글 목록 조회 시(http://localhost:8080/board) JSON 응답

    JSON

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    {
        "boards": {
            "board": [
                {
                    "board_seq": 1,
                    "board_re_ref": 0,
                    "board_re_lev": 0,
                    "board_re_seq": 0,
                    "board_writer": "게시글 작성자1",
                    "board_subject": "게시글 제목1",
                    "board_content": "게시글 내용1",
                    "board_hits": 0,
                    "del_yn": "N",
                    "ins_user_id": "TEST",
                    "ins_date": "2020-05-19 18:30:24",
                    "upd_user_id": null,
                    "upd_date": null
                },
                {
                    "board_seq": 2,
                    "board_re_ref": 0,
                    "board_re_lev": 0,
                    "board_re_seq": 0,
                    "board_writer": "게시글 작성자2",
                    "board_subject": "게시글 제목2",
                    "board_content": "게시글 내용2",
                    "board_hits": 0,
                    "del_yn": "N",
                    "ins_user_id": "TEST",
                    "ins_date": "2020-05-19 18:30:24",
                    "upd_user_id": null,
                    "upd_date": null
                },
                {
                    "board_seq": 3,
                    "board_re_ref": 0,
                    "board_re_lev": 0,
                    "board_re_seq": 0,
                    "board_writer": "게시글 작성자3",
                    "board_subject": "게시글 제목3",
                    "board_content": "게시글 내용3",
                    "board_hits": 0,
                    "del_yn": "N",
                    "ins_user_id": "TEST",
                    "ins_date": "2020-05-19 18:30:24",
                    "upd_user_id": null,
                    "upd_date": null
                },
                {
                    "board_seq": 4,
                    "board_re_ref": 0,
                    "board_re_lev": 0,
                    "board_re_seq": 0,
                    "board_writer": "게시글 작성자4",
                    "board_subject": "게시글 제목4",
                    "board_content": "게시글 내용4",
                    "board_hits": 0,
                    "del_yn": "N",
                    "ins_user_id": "TEST",
                    "ins_date": "2020-05-19 18:30:24",
                    "upd_user_id": null,
                    "upd_date": null
                },
                {
                    "board_seq": 5,
                    "board_re_ref": 0,
                    "board_re_lev": 0,
                    "board_re_seq": 0,
                    "board_writer": "게시글 작성자5",
                    "board_subject": "게시글 제목5",
                    "board_content": "게시글 내용5",
                    "board_hits": 0,
                    "del_yn": "N",
                    "ins_user_id": "TEST",
                    "ins_date": "2020-05-19 18:30:24",
                    "upd_user_id": null,
                    "upd_date": null
                }
            ]
        }
    }
    cs

     

    소스 코드는 Github Repository - https://github.com/tychejin1218/api-board_v1 (branch : section11) 를 참조하세요.

    Github에서 프로젝트 가져오기 - https://tychejin.tistory.com/33

    반응형

    댓글

Designed by Tistory.