728x90

Bean Validation 1.0 은 JSR-303이고 Bean Validation 2.0 은 JSR-380이다.

우리는 특정 값이 null 을 허용하지 않고 싶다. 그러면 아래와 같이 @notnull 하나 추가하면 바로 해결된다.
@Data
@ApiModel(description="User Model")
public class User {

    @ApiModelProperty("user 식별자")
    private Long id;

    @NotNull
    @ApiModelProperty("user 이름")
    private String name;

    @NotNull
    @ApiModelProperty("user 나이")
    private Integer age;

}

 

체크해야 되는 부분에서 @Valid 어노테이션을 달아준다.
@PostMapping("/")
@ApiOperation(value = "사용자 생성", notes = "user model 세팅된 값으로 사용자 생성")
public String postUser(@Valid @RequestBody User user) {
    users.put(user.getId(), user);
    return "success";
}

cUrl 요청해본다.

curl -X POST \
  http://localhost:8080/users/ \
  -H 'Content-Type: application/json' \
  -H 'Postman-Token: 72745d04-caa5-44a1-be84-ba9c115f4dfb' \
  -H 'cache-control: no-cache' \
  -d '{
}'

결과는 ???

{
    "timestamp": "2022-12-21T05:45:19.221+0000",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "NotNull.user.age",
                "NotNull.age",
                "NotNull.java.lang.Integer",
                "NotNull"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.age",
                        "age"
                    ],
                    "arguments": null,
                    "defaultMessage": "age",
                    "code": "age"
                }
            ],
            "defaultMessage": "null을 허용안함",
            "objectName": "user",
            "field": "age",
            "rejectedValue": null,
            "bindingFailure": false,
            "code": "NotNull"
        },
        {
            "codes": [
                "NotNull.user.name",
                "NotNull.name",
                "NotNull.java.lang.String",
                "NotNull"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.name",
                        "name"
                    ],
                    "arguments": null,
                    "defaultMessage": "name",
                    "code": "name"
                }
            ],
            "defaultMessage": "null을 허용안함",
            "objectName": "user",
            "field": "name",
            "rejectedValue": null,
            "bindingFailure": false,
            "code": "NotNull"
        }
    ],
    "message": "Validation failed for object='user'. Error count: 2",
    "path": "/users/"
}
다른것도 체크해보자. 문자열 길이, 수자크기, 포맷 등.
@Data
@ApiModel(description="User Model")
public class User {

    @ApiModelProperty("user 식별자")
    private Long id;

    @NotNull
    @Size(min = 2, max = 10)
    @ApiModelProperty("user 이름")
    private String name;

    @NotNull
    @Max(100)
    @Min(18)
    @ApiModelProperty("user 나이")
    private Integer age;

    @NotNull
    @Email
    @ApiModelProperty("user 메일")
    private String email;

}

요청

curl -X POST \
  http://localhost:8080/users/ \
  -H 'Content-Type: application/json' \
  -H 'Postman-Token: 114db0f0-bdce-4ba5-baf6-01e5104a68a3' \
  -H 'cache-control: no-cache' \
  -d '{
    "name": "abcdefg",
    "age": 17,
    "email": "aaaa"
}'

응답

{
    "timestamp": "2022-12-21T06:24:30.518+0000",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "Size.user.name",
                "Size.name",
                "Size.java.lang.String",
                "Size"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.name",
                        "name"
                    ],
                    "arguments": null,
                    "defaultMessage": "name",
                    "code": "name"
                },
                5,
                2
            ],
            "defaultMessage": "2와10사이",
            "objectName": "user",
            "field": "name",
            "rejectedValue": "abcdefg",
            "bindingFailure": false,
            "code": "Size"
        },
        {
            "codes": [
                "Min.user.age",
                "Min.age",
                "Min.java.lang.Integer",
                "Min"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.age",
                        "age"
                    ],
                    "arguments": null,
                    "defaultMessage": "age",
                    "code": "age"
                },
                10
            ],
            "defaultMessage": "18보다 작을수 없습니다.",
            "objectName": "user",
            "field": "age",
            "rejectedValue": 8,
            "bindingFailure": false,
            "code": "Min"
        },
        {
            "codes": [
                "Email.user.email",
                "Email.email",
                "Email.java.lang.String",
                "Email"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.email",
                        "email"
                    ],
                    "arguments": null,
                    "defaultMessage": "email",
                    "code": "email"
                },
                [],
                {
                    "defaultMessage": ".*",
                    "codes": [
                        ".*"
                    ],
                    "arguments": null
                }
            ],
            "defaultMessage": "메일격식이 아닙니다.",
            "objectName": "user",
            "field": "email",
            "rejectedValue": "aaaa",
            "bindingFailure": false,
            "code": "Email"
        }
    ],
    "message": "Validation failed for object='user'. Error count: 3",
    "path": "/users/"
}
이제 더 이상 parameter 하나씩 체크하기 위해서 if ...else  지옥에서 벗어나자.
 
끝!
728x90

오피셜 프로젝트 : https://github.com/SpringForAll/spring-boot-starter-swagger 라고 하네

 

일단 관련 Library 부터 추가해 보자, 22년 12월 21일 기준으로 아직도 해당 버전이 제일 인기가 많은것 같다.

<!-- https://mvnrepository.com/artifact/com.spring4all/swagger-spring-boot-starter -->
<dependency>
    <groupId>com.spring4all</groupId>
    <artifactId>swagger-spring-boot-starter</artifactId>
    <version>1.9.0.RELEASE</version>
</dependency>

application.properties 작성해 보자 

swagger.title=spring-boot-starter-swagger
swagger.description=Starter for swagger 2.x
swagger.version=1.4.0.RELEASE
swagger.license=Apache License, Version 2.0
swagger.licenseUrl=https://www.apache.org/licenses/LICENSE-2.0.html
swagger.termsOfServiceUrl=https://github.com/dyc87112/spring-boot-starter-swagger
swagger.contact.name=blake
swagger.contact.url=https://rainsister.tistory.com
swagger.contact.email=dyc87112@qq.com
swagger.base-package=com.blake
swagger.base-path=/**

혹은 application.yml 사용법은 

swagger:
    base-package: com.http:rainsister
    base-path: /**
    contact:
        email: luxury515@naver.com
        name: blake
        url: https://rainsister.tistory.com
    description: Starter for swagger 2.x
    license: Apache License, Version 2.0
    licenseUrl: https://www.apache.org/licenses/LICENSE-2.0.html
    termsOfServiceUrl: 
    title: spring-boot-starter-swagger
    version: 1.4.0.RELEASE

세팅 완료!

프로젝트를 구동후  http://localhost:8080/swagger-ui.html 에 접속하면

끝!

728x90
User 에 대하여 RESTful API를 만들어본다.
아주 기본적인 User 객체부터 만들어보자.
@Data
public class User {

    private Long id;
    private String name;
    private Integer age;

}
어 ? @Data 저거는 뭐지? 하는 분들을 위해 lombok 이라는 라이브러리를 간단하게 설명드린다.
lombok 은 java에서 객체를 만들때 getter , setter 코드를 컴파일 단계에서 자동으로 만들어주는 라이브러리다. 이놈 없이도 개발 가능하지만 getter, setter 만 만들다가 중도에 포기할수도 있다.
 
사용하고자 하는 library를 추가 ,다들 dependency를 추가한다고 하지.
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

controller 작성

@RestController
@RequestMapping(value = "/users")
public class UserController {

    static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());

    @GetMapping("/")
    public List<User> getUserList() {
    
        List<User> r = new ArrayList<User>(users.values());
        return r;
    }

    @PostMapping("/")
    public String postUser(@RequestBody User user) {
    
        users.put(user.getId(), user);
        return "success";
    }

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        
        return users.get(id);
    }


    @PutMapping("/{id}")
    public String putUser(@PathVariable Long id, @RequestBody User user) {
        User u = users.get(id);
        u.setName(user.getName());
        u.setAge(user.getAge());
        users.put(id, u);
        return "success";
    }

    
    @DeleteMapping("/{id}")
    public String deleteUser(@PathVariable Long id) {
        users.remove(id);
        return "success";
    }

}

만들었으니 테스트도 해보자

@RunWith(SpringRunner.class)
@SpringBootTest
public class Chapter21ApplicationTests {

    private MockMvc mvc;

    @Before
    public void setUp() {
        mvc = MockMvcBuilders.standaloneSetup(new UserController()).build();
    }

    @Test
    public void testUserController() throws Exception {
        RequestBuilder request;

        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[]")));

        request = post("/users/")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"id\":1,\"name\":\"테스트 합니다.\",\"age\":20}");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));

        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"테스트 합니다.\",\"age\":20}]")));

        request = put("/users/1")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"name\":\"테스트 합니다.\",\"age\":30}");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));

        request = get("/users/1");
        mvc.perform(request)
                .andExpect(content().string(equalTo("{\"id\":1,\"name\":\"테스트 합니다.\",\"age\":30}")));

        request = delete("/users/1");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));

        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[]")));

    }

}
MockMvc 를 처음 사용해 보시는 분이라면 일부 함수가 존재 하지 않는다는 메시지가 있을것이다. 상단에 아래 패키지들이 잘 import 되었는지도 살피자!
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

끝!

728x90

 

Spring 에서 @Transactional 사용하기 전에 @EnableTransactionManagement 사용했었다.

그래서 인지 몰라서 그시절 spring 쌉고수들이 springboot 으로 갈아타면서 @EnableTransactionManagement 을 꼭 써야한다는 주장을 하는걸 봐서 오늘 한판하고 왔다.

그래서 오늘 포스팅은 springboot 기준으로 @EnableTransactionManagement 을 써야 될지한번 보자!

springboot 의 메인인 @SpringBootApplication 을 타고 들어가보자.

다시 @EnableAutoConfiguration 도 타고 들어가보자.

 

이런것도 있네

여기에서 확인할수 있는것처럼 트랜잭션관리는 AOP 기반으로 JDK Dynamic Proxy와 CGLIB 2가지 방식을 정의하였다.

 

결론 : Spring 아닌 Springboot 에서는 자동으로 트랜잭션구성을 해주기때문에 @EnableTransactionManagement 를 달아주는 뻘짓은 이제 좀 자제하자!

 

반박시 니말 다 맞음 ( 너무 싼티났나 ? ^^ )

끝!

+ Recent posts