add bash scripts for git operations and Lombok documentation
This commit is contained in:
13
app.sh
Executable file
13
app.sh
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
gitup)
|
||||||
|
echo "Pushing to git"
|
||||||
|
git add -A
|
||||||
|
git commit -m $(date "+%Y-%m-%dT%H:%M:%S")
|
||||||
|
git push origin
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Launching editor"
|
||||||
|
code .
|
||||||
|
esac
|
||||||
12
docs/app.sh
Executable file
12
docs/app.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
gitup)
|
||||||
|
echo "Pushing to git"
|
||||||
|
git add -A
|
||||||
|
git commit -m $(date "+%Y-%m-%dT%H:%M:%S")
|
||||||
|
git push origin
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
code .
|
||||||
|
esac
|
||||||
433
docs/jackson.md
Normal file
433
docs/jackson.md
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
자바의 Jackson에 대해 설명하는 글을 작성하겠습니다. Jackson은 자바에서 JSON 데이터를 처리하기 위해 널리 사용되는 라이브러리로, 객체와 JSON 간의 직렬화(Serialization) 및 역직렬화(Deserialization)를 쉽게 수행할 수 있게 해줍니다. 특히, Jackson의 어노테이션은 이러한 변환 과정을 세밀하게 제어할 수 있도록 도와줍니다. 아래에서는 Jackson의 주요 어노테이션을 표로 정리한 뒤, 각 어노테이션에 대한 상세 설명과 예시를 제공하겠습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Jackson 주요 어노테이션 표
|
||||||
|
|
||||||
|
| 어노테이션 | 설명 |
|
||||||
|
|----------------------------|------------------------------------------------------------------------------------------|
|
||||||
|
| `@JsonProperty` | JSON 키와 자바 필드/메서드 이름을 매핑하거나, 직렬화/역직렬화 시 속성 이름을 지정 |
|
||||||
|
| `@JsonIgnore` | 직렬화 및 역직렬화 시 특정 필드를 무시 |
|
||||||
|
| `@JsonIgnoreProperties` | 클래스 레벨에서 직렬화/역직렬화 시 무시할 속성(들)을 지정 |
|
||||||
|
| `@JsonInclude` | 직렬화 시 특정 조건(예: null 값 제외)에 따라 필드를 포함하거나 제외 |
|
||||||
|
| `@JsonCreator` | 역직렬화 시 사용할 생성자나 팩토리 메서드를 지정 |
|
||||||
|
| `@JsonSetter` | 역직렬화 시 특정 Setter 메서드를 JSON 속성과 매핑 |
|
||||||
|
| `@JsonGetter` | 직렬화 시 특정 Getter 메서드를 JSON 속성으로 사용 |
|
||||||
|
| `@JsonAnySetter` | 정의되지 않은 JSON 속성을 Map 형태로 처리 |
|
||||||
|
| `@JsonAnyGetter` | Map의 내용을 추가적인 JSON 속성으로 직렬화 |
|
||||||
|
| `@JsonManagedReference` | 객체 간 순환 참조에서 부모 객체를 직렬화 |
|
||||||
|
| `@JsonBackReference` | 객체 간 순환 참조에서 자식 객체를 직렬화에서 제외 |
|
||||||
|
| `@JsonIdentityInfo` | 순환 참조 문제를 해결하기 위해 객체의 고유 식별자를 사용 |
|
||||||
|
| `@JsonTypeInfo` | 다형성 타입 정보를 JSON에 포함시켜 직렬화/역직렬화 시 올바른 타입으로 처리 |
|
||||||
|
| `@JsonSubTypes` | `@JsonTypeInfo`와 함께 사용되며, 하위 타입을 명시 |
|
||||||
|
| `@JsonView` | 특정 뷰(View)에 따라 직렬화/역직렬화 대상을 제한 |
|
||||||
|
| `@JacksonInject` | JSON 데이터 대신 외부에서 주입된 값을 필드에 설정 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 각 어노테이션 상세 설명 및 예시
|
||||||
|
|
||||||
|
#### 1. `@JsonProperty`
|
||||||
|
- **설명**: JSON 속성 이름과 자바 필드/메서드 이름을 매핑하거나, 이름을 강제로 지정합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
@JsonProperty("fullName")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
Person person = new Person();
|
||||||
|
person.setName("홍길동");
|
||||||
|
String json = mapper.writeValueAsString(person);
|
||||||
|
System.out.println(json); // {"fullName":"홍길동"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. `@JsonIgnore`
|
||||||
|
- **설명**: 직렬화 및 역직렬화 시 특정 필드를 제외합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
private String name;
|
||||||
|
@JsonIgnore
|
||||||
|
private int age;
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
public int getAge() { return age; }
|
||||||
|
public void setAge(int age) { this.age = age; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
Person person = new Person();
|
||||||
|
person.setName("김영희"); person.setAge(25);
|
||||||
|
String json = mapper.writeValueAsString(person);
|
||||||
|
System.out.println(json); // {"name":"김영희"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. `@JsonIgnoreProperties`
|
||||||
|
- **설명**: 클래스 레벨에서 무시할 속성을 지정하거나, 알 수 없는 속성을 무시하도록 설정합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties({"age", "ignoreUnknown = true"})
|
||||||
|
public class Person {
|
||||||
|
private String name;
|
||||||
|
private int age;
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
public int getAge() { return age; }
|
||||||
|
public void setAge(int age) { this.age = age; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
String json = "{\"name\":\"이철수\",\"age\":30,\"extra\":\"data\"}";
|
||||||
|
Person person = mapper.readValue(json, Person.class);
|
||||||
|
System.out.println(person.getName()); // "이철수"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. `@JsonInclude`
|
||||||
|
- **설명**: 직렬화 시 null 값이나 빈 값 등을 제외하는 조건을 설정합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public class Person {
|
||||||
|
private String name;
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
public String getAddress() { return address; }
|
||||||
|
public void setAddress(String address) { this.address = address; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
Person person = new Person();
|
||||||
|
person.setName("박민수");
|
||||||
|
String json = mapper.writeValueAsString(person);
|
||||||
|
System.out.println(json); // {"name":"박민수"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. `@JsonCreator`
|
||||||
|
- **설명**: 역직렬화 시 사용할 생성자나 팩토리 메서드를 지정합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public Person(@JsonProperty("name") String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
String json = "{\"name\":\"최수진\"}";
|
||||||
|
Person person = mapper.readValue(json, Person.class);
|
||||||
|
System.out.println(person.getName()); // "최수진"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. `@JsonSetter`
|
||||||
|
- **설명**: 역직렬화 시 Setter 메서드를 특정 JSON 속성과 매핑합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSetter;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@JsonSetter("fullName")
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
String json = "{\"fullName\":\"김지영\"}";
|
||||||
|
Person person = mapper.readValue(json, Person.class);
|
||||||
|
System.out.println(person.getName()); // "김지영"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 7. `@JsonGetter`
|
||||||
|
- **설명**: 직렬화 시 Getter 메서드를 JSON 속성으로 사용합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonGetter;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@JsonGetter("fullName")
|
||||||
|
public String getName() { return name; }
|
||||||
|
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
Person person = new Person();
|
||||||
|
person.setName("윤서진");
|
||||||
|
String json = mapper.writeValueAsString(person);
|
||||||
|
System.out.println(json); // {"fullName":"윤서진"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 8. `@JsonAnySetter`
|
||||||
|
- **설명**: 정의되지 않은 JSON 속성을 Map으로 처리합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
private Map<String, Object> properties = new HashMap<>();
|
||||||
|
|
||||||
|
@JsonAnySetter
|
||||||
|
public void setProperty(String key, Object value) {
|
||||||
|
properties.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getProperties() { return properties; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
String json = "{\"name\":\"이정훈\",\"age\":29}";
|
||||||
|
Person person = mapper.readValue(json, Person.class);
|
||||||
|
System.out.println(person.getProperties()); // {name=이정훈, age=29}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 9. `@JsonAnyGetter`
|
||||||
|
- **설명**: Map의 내용을 추가적인 JSON 속성으로 직렬화합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
private Map<String, Object> properties = new HashMap<>();
|
||||||
|
|
||||||
|
@JsonAnyGetter
|
||||||
|
public Map<String, Object> getProperties() { return properties; }
|
||||||
|
|
||||||
|
public void setProperty(String key, Object value) { properties.put(key, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
Person person = new Person();
|
||||||
|
person.setProperty("name", "최영미");
|
||||||
|
person.setProperty("age", 35);
|
||||||
|
String json = mapper.writeValueAsString(person);
|
||||||
|
System.out.println(json); // {"name":"최영미","age":35}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 10. `@JsonManagedReference`와 `@JsonBackReference`
|
||||||
|
- **설명**: 순환 참조 문제를 해결하기 위해 부모-자식 관계를 정의합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonManagedReference;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||||
|
|
||||||
|
public class Parent {
|
||||||
|
@JsonManagedReference
|
||||||
|
public Child child;
|
||||||
|
|
||||||
|
public void setChild(Child child) { this.child = child; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Child {
|
||||||
|
@JsonBackReference
|
||||||
|
public Parent parent;
|
||||||
|
|
||||||
|
public void setParent(Parent parent) { this.parent = parent; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
Parent parent = new Parent();
|
||||||
|
Child child = new Child();
|
||||||
|
parent.setChild(child);
|
||||||
|
child.setParent(parent);
|
||||||
|
String json = mapper.writeValueAsString(parent);
|
||||||
|
System.out.println(json); // {"child":{}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 11. `@JsonIdentityInfo`
|
||||||
|
- **설명**: 순환 참조를 객체 ID로 해결합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
|
||||||
|
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
|
||||||
|
|
||||||
|
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
|
||||||
|
public class Person {
|
||||||
|
private int id;
|
||||||
|
private String name;
|
||||||
|
private Person friend;
|
||||||
|
|
||||||
|
public Person(int id, String name) { this.id = id; this.name = name; }
|
||||||
|
public void setFriend(Person friend) { this.friend = friend; }
|
||||||
|
public String getName() { return name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
Person p1 = new Person(1, "김태양");
|
||||||
|
Person p2 = new Person(2, "이수진");
|
||||||
|
p1.setFriend(p2);
|
||||||
|
p2.setFriend(p1);
|
||||||
|
String json = mapper.writeValueAsString(p1);
|
||||||
|
System.out.println(json); // {"id":1,"name":"김태양","friend":{"id":2,"name":"이수진","friend":1}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 12. `@JsonTypeInfo`와 `@JsonSubTypes`
|
||||||
|
- **설명**: 다형성을 처리하기 위해 타입 정보를 JSON에 포함합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||||
|
@JsonSubTypes({
|
||||||
|
@Type(value = Dog.class, name = "dog"),
|
||||||
|
@Type(value = Cat.class, name = "cat")
|
||||||
|
})
|
||||||
|
public abstract class Animal {
|
||||||
|
public String name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Dog extends Animal {}
|
||||||
|
public class Cat extends Animal {}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
Dog dog = new Dog(); dog.name = "멍멍이";
|
||||||
|
String json = mapper.writeValueAsString(dog);
|
||||||
|
System.out.println(json); // {"type":"dog","name":"멍멍이"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 13. `@JsonView`
|
||||||
|
- **설명**: 특정 뷰에 따라 직렬화 대상을 제한합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
public interface PublicView {}
|
||||||
|
public interface PrivateView extends PublicView {}
|
||||||
|
|
||||||
|
@JsonView(PublicView.class)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@JsonView(PrivateView.class)
|
||||||
|
private int age;
|
||||||
|
|
||||||
|
public Person(String name, int age) { this.name = name; this.age = age; }
|
||||||
|
public String getName() { return name; }
|
||||||
|
public int getAge() { return age; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
Person person = new Person("박준영", 27);
|
||||||
|
String json = mapper.writerWithView(Person.PublicView.class).writeValueAsString(person);
|
||||||
|
System.out.println(json); // {"name":"박준영"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 14. `@JacksonInject`
|
||||||
|
- **설명**: JSON 데이터가 아닌 외부에서 주입된 값을 사용합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JacksonInject;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
@JacksonInject
|
||||||
|
private String source;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Person(String name) { this.name = name; }
|
||||||
|
public String getSource() { return source; }
|
||||||
|
public String getName() { return name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
InjectableValues inject = new InjectableValues.Std().addValue(String.class, "외부 소스");
|
||||||
|
Person person = mapper.reader(inject).forType(Person.class)
|
||||||
|
.readValue("{\"name\":\"최민수\"}");
|
||||||
|
System.out.println(person.getSource()); // "외부 소스"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 결론
|
||||||
|
Jackson은 강력한 JSON 처리 기능을 제공하며, 위의 어노테이션들은 이를 세밀하게 제어할 수 있게 해줍니다. 다형성 처리, 순환 참조 해결, 속성 이름 매핑 등 다양한 기능을 통해 개발자는 유연하고 효율적인 JSON 처리를 구현할 수 있습니다. 프로젝트에서 Jackson을 사용할 때는 팀원 모두가 관련 어노테이션을 이해하고, 적절한 설정(예: 의존성 추가, IDE 플러그인 설치)을 통해 효율적으로 활용하는 것이 중요합니다.
|
||||||
|
|
||||||
|
궁금한 점이 있다면 언제든 물어보세요!
|
||||||
315
docs/jackson_2.md
Normal file
315
docs/jackson_2.md
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
자바의 Jackson에서 XML과 JSR-310(Java Time API) 관련 기능을 다루는 글을 작성하겠습니다. Jackson은 기본적으로 JSON 처리를 위한 라이브러리지만, `jackson-dataformat-xml` 모듈을 통해 XML 데이터를 처리할 수 있으며, `jackson-datatype-jsr310` 모듈을 통해 Java 8의 JSR-310 날짜/시간 API(LocalDate, LocalDateTime 등)를 지원합니다. 이들 모듈은 JSON 처리와 유사한 방식으로 동작하지만, XML 구조나 JSR-310 데이터 타입에 특화된 어노테이션과 설정이 필요합니다.
|
||||||
|
|
||||||
|
아래에서는 XML과 JSR-310 관련 주요 어노테이션을 표로 정리한 뒤, 각 어노테이션에 대한 설명과 예시를 상세히 다루겠습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Jackson XML 및 JSR-310 관련 주요 어노테이션 표
|
||||||
|
|
||||||
|
#### XML 관련 어노테이션 (jackson-dataformat-xml)
|
||||||
|
| 어노테이션 | 설명 |
|
||||||
|
|----------------------------|----------------------------------------------------------------------------------------|
|
||||||
|
| `@JacksonXmlRootElement` | XML 루트 요소의 이름과 네임스페이스를 지정 |
|
||||||
|
| `@JacksonXmlProperty` | XML 요소나 속성의 이름과 네임스페이스를 지정하며, 속성 여부를 설정 |
|
||||||
|
| `@JacksonXmlElementWrapper` | 컬렉션 필드를 감싸는 XML 요소를 지정 |
|
||||||
|
| `@JacksonXmlText` | 필드를 XML 요소의 텍스트 콘텐츠로 직렬화 |
|
||||||
|
| `@JacksonXmlCData` | 필드를 XML의 CDATA 섹션으로 직렬화 |
|
||||||
|
|
||||||
|
#### JSR-310 관련 어노테이션 (jackson-datatype-jsr310)
|
||||||
|
| 어노테이션 | 설명 |
|
||||||
|
|----------------------------|----------------------------------------------------------------------------------------|
|
||||||
|
| `@JsonFormat` | JSR-310 날짜/시간 객체의 직렬화 형식을 지정 (패턴, 시간대 등) |
|
||||||
|
| `@JsonSerialize` | JSR-310 객체를 커스터마이즈된 직렬화 방식으로 처리 |
|
||||||
|
| `@JsonDeserialize` | JSR-310 객체를 커스터마이즈된 역직렬화 방식으로 처리 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 프로젝트 설정
|
||||||
|
XML과 JSR-310 기능을 사용하려면 `pom.xml`에 다음 의존성을 추가해야 합니다:
|
||||||
|
```xml
|
||||||
|
<!-- Jackson XML 지원 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
|
<artifactId>jackson-dataformat-xml</artifactId>
|
||||||
|
<version>2.17.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- Jackson JSR-310 지원 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
|
<version>2.17.0</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 각 어노테이션 상세 설명 및 예시
|
||||||
|
|
||||||
|
#### XML 관련 어노테이션
|
||||||
|
|
||||||
|
##### 1. `@JacksonXmlRootElement`
|
||||||
|
- **설명**: XML 문서의 루트 요소 이름을 지정하며, 네임스페이스를 설정할 수 있습니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
|
||||||
|
@JacksonXmlRootElement(localName = "person", namespace = "http://example.com")
|
||||||
|
public class Person {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
XmlMapper xmlMapper = new XmlMapper();
|
||||||
|
Person person = new Person();
|
||||||
|
person.setName("홍길동");
|
||||||
|
String xml = xmlMapper.writeValueAsString(person);
|
||||||
|
System.out.println(xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: <person xmlns="http://example.com"><name>홍길동</name></person>
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 2. `@JacksonXmlProperty`
|
||||||
|
- **설명**: XML 요소나 속성의 이름을 지정하고, 속성으로 직렬화할지 여부를 설정합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
@JacksonXmlProperty(localName = "fullName")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private int age;
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
public int getAge() { return age; }
|
||||||
|
public void setAge(int age) { this.age = age; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
XmlMapper xmlMapper = new XmlMapper();
|
||||||
|
Person person = new Person();
|
||||||
|
person.setName("김영희");
|
||||||
|
person.setAge(25);
|
||||||
|
String xml = xmlMapper.writeValueAsString(person);
|
||||||
|
System.out.println(xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: <Person age="25"><fullName>김영희</fullName></Person>
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 3. `@JacksonXmlElementWrapper`
|
||||||
|
- **설명**: 컬렉션 필드를 감싸는 XML 요소를 지정합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
@JacksonXmlElementWrapper(localName = "hobbies")
|
||||||
|
@JacksonXmlProperty(localName = "hobby")
|
||||||
|
private List<String> hobbies;
|
||||||
|
|
||||||
|
public List<String> getHobbies() { return hobbies; }
|
||||||
|
public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
XmlMapper xmlMapper = new XmlMapper();
|
||||||
|
Person person = new Person();
|
||||||
|
person.setHobbies(Arrays.asList("축구", "게임"));
|
||||||
|
String xml = xmlMapper.writeValueAsString(person);
|
||||||
|
System.out.println(xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: <Person><hobbies><hobby>축구</hobby><hobby>게임</hobby></hobbies></Person>
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 4. `@JacksonXmlText`
|
||||||
|
- **설명**: 필드를 XML 요소의 텍스트 콘텐츠로 직렬화합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
|
||||||
|
public class Note {
|
||||||
|
@JacksonXmlText
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
public String getContent() { return content; }
|
||||||
|
public void setContent(String content) { this.content = content; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
XmlMapper xmlMapper = new XmlMapper();
|
||||||
|
Note note = new Note();
|
||||||
|
note.setContent("안녕하세요");
|
||||||
|
String xml = xmlMapper.writeValueAsString(note);
|
||||||
|
System.out.println(xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: <Note>안녕하세요</Note>
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 5. `@JacksonXmlCData`
|
||||||
|
- **설명**: 필드를 CDATA 섹션으로 감싸서 직렬화합니다. HTML 태그 등 특수 문자를 포함할 때 유용합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
|
||||||
|
public class Message {
|
||||||
|
@JacksonXmlProperty(localName = "text")
|
||||||
|
@JacksonXmlCData
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
public String getContent() { return content; }
|
||||||
|
public void setContent(String content) { this.content = content; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
XmlMapper xmlMapper = new XmlMapper();
|
||||||
|
Message message = new Message();
|
||||||
|
message.setContent("<b>중요</b>");
|
||||||
|
String xml = xmlMapper.writeValueAsString(message);
|
||||||
|
System.out.println(xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: <Message><text><![CDATA[<b>중요</b>]]></text></Message>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### JSR-310 관련 어노테이션
|
||||||
|
|
||||||
|
##### 1. `@JsonFormat`
|
||||||
|
- **설명**: JSR-310 날짜/시간 객체의 직렬화 형식을 지정합니다. `pattern` 속성을 사용하여 원하는 포맷을 정의합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
public class Event {
|
||||||
|
@JsonFormat(pattern = "yyyy/MM/dd")
|
||||||
|
private LocalDate date;
|
||||||
|
|
||||||
|
public LocalDate getDate() { return date; }
|
||||||
|
public void setDate(LocalDate date) { this.date = date; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.registerModule(new JavaTimeModule());
|
||||||
|
Event event = new Event();
|
||||||
|
event.setDate(LocalDate.of(2025, 2, 27));
|
||||||
|
String json = mapper.writeValueAsString(event);
|
||||||
|
System.out.println(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: {"date":"2025/02/27"}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 2. `@JsonSerialize`
|
||||||
|
- **설명**: JSR-310 객체를 사용자 정의 직렬화 방식으로 처리합니다. `JavaTimeModule`과 함께 커스터마이징할 때 유용합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
public class Event {
|
||||||
|
@JsonSerialize(using = LocalDateSerializer.class)
|
||||||
|
private LocalDate date;
|
||||||
|
|
||||||
|
public LocalDate getDate() { return date; }
|
||||||
|
public void setDate(LocalDate date) { this.date = date; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.registerModule(new JavaTimeModule());
|
||||||
|
Event event = new Event();
|
||||||
|
event.setDate(LocalDate.of(2025, 2, 27));
|
||||||
|
String json = mapper.writeValueAsString(event);
|
||||||
|
System.out.println(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: {"date":"2025-02-27"} (기본 ISO 포맷)
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 3. `@JsonDeserialize`
|
||||||
|
- **설명**: JSR-310 객체를 사용자 정의 역직렬화 방식으로 처리합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import com.fasterxml.jackson.annotation.JsonDeserialize;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
public class Event {
|
||||||
|
@JsonDeserialize(using = LocalDateDeserializer.class)
|
||||||
|
private LocalDate date;
|
||||||
|
|
||||||
|
public LocalDate getDate() { return date; }
|
||||||
|
public void setDate(LocalDate date) { this.date = date; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.registerModule(new JavaTimeModule());
|
||||||
|
String json = "{\"date\":\"2025-02-27\"}";
|
||||||
|
Event event = mapper.readValue(json, Event.class);
|
||||||
|
System.out.println(event.getDate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: 2025-02-27
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 추가 설정 및 주의사항
|
||||||
|
1. **XML 처리**: `XmlMapper`를 사용하여 XML을 직렬화/역직렬화하며, JSON과 달리 계층적 구조를 잘 표현해야 합니다.
|
||||||
|
```java
|
||||||
|
XmlMapper xmlMapper = new XmlMapper();
|
||||||
|
```
|
||||||
|
2. **JSR-310 모듈 등록**: `JavaTimeModule`을 `ObjectMapper`에 등록해야 JSR-310 타입이 기본적으로 지원됩니다.
|
||||||
|
```java
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.registerModule(new JavaTimeModule());
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 결론
|
||||||
|
Jackson의 XML 및 JSR-310 지원은 JSON 처리 능력을 확장하여 다양한 데이터 형식을 다룰 수 있게 해줍니다. XML 관련 어노테이션은 XML의 요소, 속성, 텍스트를 세밀히 제어하며, JSR-310 관련 어노테이션은 날짜/시간 데이터의 직렬화 형식을 사용자 정의할 수 있게 합니다. 프로젝트에서 이를 활용하려면 적절한 모듈 의존성을 추가하고, `XmlMapper` 또는 `ObjectMapper`를 올바르게 설정하는 것이 중요합니다.
|
||||||
|
|
||||||
|
추가 질문이 있다면 언제든 물어보세요!
|
||||||
280
docs/java-core.md
Normal file
280
docs/java-core.md
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
자바에서 기본적으로 자주 사용되는 어노테이션들을 정리하겠습니다. 여기서 "기본적으로 자주 사용되는"이란 자바 표준 API(JDK)와 스프링 프레임워크, 그리고 일반적인 ORM(JPA) 환경에서 널리 활용되는 어노테이션을 기준으로 하겠습니다. 이들은 자바 개발에서 필수적인 기능(예: 클래스/메서드 정의, DI, 데이터베이스 매핑 등)을 제공하며, 특별한 외부 라이브러리 없이도 자주 등장합니다.
|
||||||
|
|
||||||
|
아래에서 주요 어노테이션을 표 형식으로 정리한 뒤, 각 어노테이션에 대한 설명과 예시를 제공하겠습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 자주 사용되는 어노테이션 표
|
||||||
|
|
||||||
|
| 어노테이션 | 설명 | 출처 |
|
||||||
|
|-----------------------|----------------------------------------------------------------------------------------|---------------------|
|
||||||
|
| `@Override` | 메서드가 상위 클래스나 인터페이스의 메서드를 재정의했음을 명시 | JDK (java.lang) |
|
||||||
|
| `@Deprecated` | 해당 요소가 더 이상 권장되지 않음을 표시 | JDK (java.lang) |
|
||||||
|
| `@SuppressWarnings` | 컴파일러 경고를 억제 | JDK (java.lang) |
|
||||||
|
| `@FunctionalInterface`| 인터페이스가 단일 추상 메서드를 가지는 함수형 인터페이스임을 명시 | JDK (java.lang) |
|
||||||
|
| `@SafeVarargs` | 가변 인자 메서드에서 타입 안전성을 보장함을 명시 | JDK (java.lang) |
|
||||||
|
| `@Retention` | 어노테이션의 유지 범위를 지정 (SOURCE, CLASS, RUNTIME) | JDK (java.lang.annotation) |
|
||||||
|
| `@Target` | 어노테이션이 적용될 수 있는 대상을 지정 | JDK (java.lang.annotation) |
|
||||||
|
| `@Documented` | 어노테이션이 Javadoc에 포함되도록 지정 | JDK (java.lang.annotation) |
|
||||||
|
| `@Inherited` | 어노테이션이 하위 클래스에 상속되도록 지정 | JDK (java.lang.annotation) |
|
||||||
|
| `@SpringBootApplication` | 스프링 부트 애플리케이션의 메인 클래스임을 나타내며, 여러 설정을 포함 | Spring Boot |
|
||||||
|
| `@Autowired` | 의존성 주입(DI)을 자동으로 수행 | Spring Framework |
|
||||||
|
| `@RestController` | RESTful 웹 서비스 컨트롤러임을 지정 | Spring Framework |
|
||||||
|
| `@RequestMapping` | HTTP 요청을 메서드나 클래스에 매핑 | Spring Framework |
|
||||||
|
| `@GetMapping` | HTTP GET 요청을 메서드에 매핑 | Spring Framework |
|
||||||
|
| `@PostMapping` | HTTP POST 요청을 메서드에 매핑 | Spring Framework |
|
||||||
|
| `@Entity` | 클래스가 JPA 엔티티임을 나타냄 | JPA (jakarta.persistence) |
|
||||||
|
| `@Id` | 엔티티의 기본 키(PK)를 지정 | JPA (jakarta.persistence) |
|
||||||
|
| `@Column` | 필드가 데이터베이스 컬럼에 매핑됨을 지정 | JPA (jakarta.persistence) |
|
||||||
|
| `@Table` | 엔티티가 매핑될 데이터베이스 테이블을 지정 | JPA (jakarta.persistence) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 각 어노테이션 상세 설명 및 예시
|
||||||
|
|
||||||
|
#### 1. `@Override`
|
||||||
|
- **설명**: 메서드가 상위 클래스나 인터페이스의 메서드를 재정의했음을 컴파일러에 알립니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
class Parent {
|
||||||
|
void display() { System.out.println("부모"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Child extends Parent {
|
||||||
|
@Override
|
||||||
|
void display() { System.out.println("자식"); }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. `@Deprecated`
|
||||||
|
- **설명**: 메서드나 클래스가 더 이상 사용되지 않음을 표시하며, 사용 시 경고를 발생시킵니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
class OldClass {
|
||||||
|
@Deprecated
|
||||||
|
void oldMethod() { System.out.println("구형 메서드"); }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. `@SuppressWarnings`
|
||||||
|
- **설명**: 특정 경고(예: "unchecked", "deprecated")를 억제합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List rawList = new ArrayList(); // 경고 억제
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. `@FunctionalInterface`
|
||||||
|
- **설명**: 단일 추상 메서드를 가진 인터페이스를 함수형 인터페이스로 표시합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
@FunctionalInterface
|
||||||
|
interface MyFunction {
|
||||||
|
void run();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. `@SafeVarargs`
|
||||||
|
- **설명**: 가변 인자 메서드가 타입 안전함을 보장합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
@SafeVarargs
|
||||||
|
static <T> void printAll(T... items) {
|
||||||
|
for (T item : items) System.out.println(item);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. `@Retention`
|
||||||
|
- **설명**: 어노테이션의 유지 범위를 지정합니다 (SOURCE: 소스 코드, CLASS: 바이트 코드, RUNTIME: 런타임).
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface MyAnnotation {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 7. `@Target`
|
||||||
|
- **설명**: 어노테이션이 적용될 수 있는 위치(예: 메서드, 클래스 등)를 지정합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@interface MyMethodAnnotation {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 8. `@Documented`
|
||||||
|
- **설명**: 어노테이션이 Javadoc에 포함되도록 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
|
||||||
|
@Documented
|
||||||
|
@interface MyDocAnnotation {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 9. `@Inherited`
|
||||||
|
- **설명**: 어노테이션이 하위 클래스에 상속됩니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
|
||||||
|
@Inherited
|
||||||
|
@interface MyInheritedAnnotation {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 10. `@SpringBootApplication`
|
||||||
|
- **설명**: 스프링 부트 애플리케이션의 진입점을 정의하며, `@Configuration`, `@EnableAutoConfiguration`, `@ComponentScan`을 포함합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 11. `@Autowired`
|
||||||
|
- **설명**: 스프링의 의존성 주입을 자동으로 수행합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
public class UserService {
|
||||||
|
@Autowired
|
||||||
|
private UserRepository repository;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 12. `@RestController`
|
||||||
|
- **설명**: REST API 컨트롤러를 정의하며, `@Controller`와 `@ResponseBody`를 결합한 형태입니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class HelloController {
|
||||||
|
@GetMapping("/hello")
|
||||||
|
public String hello() { return "Hello, World!"; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 13. `@RequestMapping`
|
||||||
|
- **설명**: HTTP 요청을 클래스나 메서드에 매핑합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api")
|
||||||
|
public class ApiController {
|
||||||
|
@RequestMapping("/test")
|
||||||
|
public String test() { return "API 테스트"; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 14. `@GetMapping`
|
||||||
|
- **설명**: HTTP GET 요청을 메서드에 매핑합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class UserController {
|
||||||
|
@GetMapping("/users")
|
||||||
|
public String getUsers() { return "유저 목록"; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 15. `@PostMapping`
|
||||||
|
- **설명**: HTTP POST 요청을 메서드에 매핑합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class UserController {
|
||||||
|
@PostMapping("/users")
|
||||||
|
public String createUser(@RequestBody String user) { return "유저 생성: " + user; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 16. `@Entity`
|
||||||
|
- **설명**: 클래스가 JPA 엔티티임을 나타냅니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class User {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 17. `@Id`
|
||||||
|
- **설명**: 엔티티의 기본 키를 지정합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Product {
|
||||||
|
@Id
|
||||||
|
private Long productId;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 18. `@Column`
|
||||||
|
- **설명**: 필드가 매핑될 데이터베이스 컬럼을 정의합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Employee {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "full_name")
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 19. `@Table`
|
||||||
|
- **설명**: 엔티티가 매핑될 데이터베이스 테이블을 지정합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "customers")
|
||||||
|
public class Customer {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 결론
|
||||||
|
위 어노테이션들은 자바 개발에서 필수적인 역할을 합니다. JDK 제공 어노테이션은 기본적인 코드 품질과 가독성을 높이고, 스프링 프레임워크 어노테이션은 의존성 주입과 웹 애플리케이션 개발을 간소화하며, JPA 어노테이션은 데이터베이스와 객체 간 매핑을 쉽게 처리합니다. 프로젝트 요구사항에 따라 적절히 선택하고 사용하면 개발 효율성과 유지보수성을 크게 향상시킬 수 있습니다.
|
||||||
|
|
||||||
|
추가로 궁금한 점이 있다면 말씀해주세요!
|
||||||
274
docs/javadoc.md
Normal file
274
docs/javadoc.md
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
자바독(Javadoc)은 자바 소스 코드에서 문서를 생성하기 위한 도구로, 주로 클래스, 인터페이스, 메서드, 필드 등에 대한 설명을 HTML 형식으로 생성합니다. Javadoc은 특정 태그(속성)를 사용하여 코드에 주석을 추가하며, 이를 기반으로 문서를 작성합니다. 아래에서는 Javadoc에서 지원하는 모든 속성(태그)을 표 형식으로 정리하고, 문서화를 잘하는 팁을 제공하겠습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Javadoc에서 지원하는 속성(태그) 표
|
||||||
|
|
||||||
|
| 태그 | 설명 | 적용 대상 |
|
||||||
|
|-----------------------|----------------------------------------------------------------------------------------|-----------------------|
|
||||||
|
| `@author` | 코드 작성자를 지정 | 클래스, 인터페이스 |
|
||||||
|
| `@version` | 코드의 버전을 명시 | 클래스, 인터페이스 |
|
||||||
|
| `@since` | 해당 요소가 추가된 버전이나 날짜를 명시 | 모든 요소 |
|
||||||
|
| `@deprecated` | 해당 요소가 더 이상 권장되지 않음을 표시 (Java의 `@Deprecated`와 함께 사용 가능) | 모든 요소 |
|
||||||
|
| `@see` | 관련 클래스, 메서드, 필드 등의 참조를 제공 | 모든 요소 |
|
||||||
|
| `@link` | 인라인 텍스트 내에서 다른 요소로의 하이퍼링크를 생성 | 모든 요소 (인라인) |
|
||||||
|
| `@linkplain` | `@link`와 유사하지만, 링크 텍스트를 코드 스타일이 아닌 일반 텍스트로 표시 | 모든 요소 (인라인) |
|
||||||
|
| `@param` | 메서드의 매개변수를 설명 | 메서드, 생성자 |
|
||||||
|
| `@return` | 메서드의 반환값을 설명 | 메서드 |
|
||||||
|
| `@throws` / `@exception` | 메서드가 던질 수 있는 예외를 설명 | 메서드, 생성자 |
|
||||||
|
| `@value` | 상수 필드의 값을 인라인으로 참조 | 필드 (static final) |
|
||||||
|
| `@code` | 텍스트를 코드 스타일(모노스페이스)로 표시하며, HTML 태그를 이스케이프 처리 | 모든 요소 (인라인) |
|
||||||
|
| `@literal` | 텍스트를 그대로 표시하며, HTML 태그를 이스케이프 처리 | 모든 요소 (인라인) |
|
||||||
|
| `{@inheritDoc}` | 상위 클래스나 인터페이스의 Javadoc 설명을 상속 | 메서드, 클래스 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 각 태그 상세 설명 및 예시
|
||||||
|
|
||||||
|
#### 1. `@author`
|
||||||
|
- **설명**: 코드 작성자를 나타냅니다. 여러 명일 경우 반복 사용 가능.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 사용자 관리 클래스
|
||||||
|
* @author 홍길동
|
||||||
|
* @author 김영희
|
||||||
|
*/
|
||||||
|
public class UserManager {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. `@version`
|
||||||
|
- **설명**: 코드의 버전을 명시합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 계산기 유틸리티
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
public class Calculator {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. `@since`
|
||||||
|
- **설명**: 해당 요소가 추가된 시점을 기록합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 사용자 ID 반환
|
||||||
|
* @since 2023-01-01
|
||||||
|
*/
|
||||||
|
public Long getUserId() { return 1L; }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. `@deprecated`
|
||||||
|
- **설명**: 더 이상 사용되지 않는 요소임을 알립니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 구형 메서드
|
||||||
|
* @deprecated 대신 {@link #newMethod()}를 사용하세요.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void oldMethod() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. `@see`
|
||||||
|
- **설명**: 관련된 참조를 추가합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 사용자 정보 클래스
|
||||||
|
* @see java.util.List
|
||||||
|
* @see UserManager#getUser()
|
||||||
|
*/
|
||||||
|
public class User {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. `@link`
|
||||||
|
- **설명**: 인라인 링크를 생성합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 사용자를 추가합니다. 자세한 내용은 {@link UserManager}를 참조하세요.
|
||||||
|
*/
|
||||||
|
public void addUser() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 7. `@linkplain`
|
||||||
|
- **설명**: `@link`와 비슷하지만 코드 스타일이 아닌 일반 텍스트로 표시.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 사용자를 삭제합니다. {@linkplain UserManager}에서 관리합니다.
|
||||||
|
*/
|
||||||
|
public void deleteUser() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 8. `@param`
|
||||||
|
- **설명**: 메서드 매개변수를 설명합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 두 수를 더합니다.
|
||||||
|
* @param a 첫 번째 수
|
||||||
|
* @param b 두 번째 수
|
||||||
|
*/
|
||||||
|
public int add(int a, int b) { return a + b; }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 9. `@return`
|
||||||
|
- **설명**: 메서드 반환값을 설명합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 사용자 이름을 반환합니다.
|
||||||
|
* @return 사용자 이름 문자열
|
||||||
|
*/
|
||||||
|
public String getName() { return "홍길동"; }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 10. `@throws` / `@exception`
|
||||||
|
- **설명**: 메서드가 발생시킬 수 있는 예외를 설명합니다 (동일한 기능).
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 파일을 읽습니다.
|
||||||
|
* @throws IOException 파일 읽기 실패 시 발생
|
||||||
|
*/
|
||||||
|
public void readFile() throws IOException {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 11. `@value`
|
||||||
|
- **설명**: 상수 필드의 값을 인라인으로 참조합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 최대 사용자 수: {@value}
|
||||||
|
*/
|
||||||
|
public static final int MAX_USERS = 100;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 12. `@code`
|
||||||
|
- **설명**: 코드 스타일로 텍스트를 표시하며, HTML 태그를 이스케이프합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* {@code null}을 반환할 수 있습니다.
|
||||||
|
*/
|
||||||
|
public String getValue() { return null; }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 13. `@literal`
|
||||||
|
- **설명**: 텍스트를 그대로 표시하며, HTML 태그를 이스케이프합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* <b>중요</b>는 {@literal <b>중요</b>}로 표시됩니다.
|
||||||
|
*/
|
||||||
|
public void importantMethod() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 14. `{@inheritDoc}`
|
||||||
|
- **설명**: 상위 클래스나 인터페이스의 Javadoc을 상속합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() { return super.toString(); }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 문서화를 잘하는 팁
|
||||||
|
|
||||||
|
1. **간결하고 명확하게 작성**
|
||||||
|
- 불필요한 장황함을 피하고 핵심 정보만 전달하세요.
|
||||||
|
- 예: "이 메서드는 두 수를 더합니다." vs "두 정수를 더한 결과를 반환하는 함수입니다."
|
||||||
|
|
||||||
|
2. **일관된 스타일 유지**
|
||||||
|
- 팀 내에서 Javadoc 스타일을 통일하세요 (예: 문장 끝에 마침표 사용 여부, 태그 순서 등).
|
||||||
|
- 예: `@param`은 항상 먼저, `@throws`는 마지막에 배치.
|
||||||
|
|
||||||
|
3. **의미 있는 설명 제공**
|
||||||
|
- 단순히 "이 메서드는 값을 반환합니다" 대신, **왜**, **어떻게**, **언제** 사용되는지 설명하세요.
|
||||||
|
- 예:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 사용자 인증 토큰을 생성합니다.
|
||||||
|
* 로그인 성공 시 호출되며, 토큰은 24시간 유효합니다.
|
||||||
|
* @param userId 사용자 ID
|
||||||
|
* @return JWT 형식의 인증 토큰
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **태그를 적절히 활용**
|
||||||
|
- 모든 메서드에 `@param`, `@return`, `@throws`를 빠뜨리지 말고 작성하세요.
|
||||||
|
- `@see`와 `@link`로 관련 요소를 연결해 탐색성을 높이세요.
|
||||||
|
|
||||||
|
5. **코드 예제 추가**
|
||||||
|
- 복잡한 메서드에는 사용 예제를 포함하세요.
|
||||||
|
- 예:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 문자열을 분리합니다.
|
||||||
|
* 예: {@code splitString("a,b,c", ",")} -> ["a", "b", "c"]
|
||||||
|
* @param input 입력 문자열
|
||||||
|
* @param delimiter 구분자
|
||||||
|
* @return 분리된 문자열 배열
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Deprecated 요소 명확히 표시**
|
||||||
|
- `@deprecated` 태그와 함께 대체 방법을 제안하세요.
|
||||||
|
- 예:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* @deprecated 대신 {@link #newMethod()}를 사용하세요.
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
7. **HTML 태그 활용**
|
||||||
|
- `<p>`, `<ul>`, `<li>` 등을 사용해 가독성을 높이세요.
|
||||||
|
- 예:
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* <p>이 클래스는 사용자 정보를 관리합니다.</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>등록</li>
|
||||||
|
* <li>삭제</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
8. **프로젝트별 규칙 정의**
|
||||||
|
- `@since`, `@author` 등은 프로젝트 정책에 따라 선택적으로 사용하세요.
|
||||||
|
- 예: 개인 프로젝트에서는 `@author` 생략 가능.
|
||||||
|
|
||||||
|
9. **자동화 도구 활용**
|
||||||
|
- Maven이나 Gradle의 Javadoc 플러그인을 사용해 문서 생성을 자동화하세요.
|
||||||
|
- 예 (Maven):
|
||||||
|
```xml
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>3.6.3</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>javadoc</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
```
|
||||||
|
|
||||||
|
10. **정기적 리뷰**
|
||||||
|
- 코드 변경 시 Javadoc도 함께 업데이트하고, 팀 리뷰를 통해 누락된 부분을 점검하세요.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 결론
|
||||||
|
Javadoc 태그는 자바 코드의 문서화를 체계적으로 지원하며, 잘 작성된 Javadoc은 코드의 유지보수성과 협업 효율성을 크게 높입니다. 위의 태그를 적절히 사용하고, 제공된 팁을 적용하면 읽기 쉽고 유용한 문서를 만들 수 있습니다. 프로젝트의 규모와 팀의 필요에 따라 문서화 수준을 조정하되, 최소한 핵심 요소(메서드 설명, 매개변수, 반환값, 예외)는 항상 포함하는 습관을 들이세요.
|
||||||
|
|
||||||
|
추가 질문이 있다면 언제든 물어보세요!
|
||||||
359
docs/junit.md
Normal file
359
docs/junit.md
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
자바의 JUnit에 대해 설명하는 글을 작성하겠습니다. JUnit은 자바에서 단위 테스트(Unit Test)를 작성하고 실행하기 위한 가장 널리 사용되는 프레임워크입니다. 특히 JUnit 5는 모듈화된 구조와 다양한 기능을 제공하여 테스트 작성의 유연성과 가독성을 높여줍니다. JUnit은 어노테이션을 통해 테스트의 생명주기와 동작을 제어하며, 이를 통해 개발자는 테스트 코드를 간결하고 명확하게 작성할 수 있습니다.
|
||||||
|
|
||||||
|
아래에서는 JUnit 5의 주요 어노테이션을 표로 정리한 뒤, 각 어노테이션에 대한 설명과 예시를 상세히 다루겠습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### JUnit 5 주요 어노테이션 표
|
||||||
|
|
||||||
|
| 어노테이션 | 설명 |
|
||||||
|
|----------------------------|----------------------------------------------------------------------------------------|
|
||||||
|
| `@Test` | 해당 메서드가 테스트 메서드임을 나타냄 |
|
||||||
|
| `@BeforeAll` | 모든 테스트 메서드 실행 전에 한 번만 실행되는 초기화 메서드 (static 필요) |
|
||||||
|
| `@AfterAll` | 모든 테스트 메서드 실행 후에 한 번만 실행되는 정리 메서드 (static 필요) |
|
||||||
|
| `@BeforeEach` | 각 테스트 메서드 실행 전에 실행되는 초기화 메서드 |
|
||||||
|
| `@AfterEach` | 각 테스트 메서드 실행 후에 실행되는 정리 메서드 |
|
||||||
|
| `@Disabled` | 테스트 클래스나 메서드를 비활성화하여 실행하지 않음 |
|
||||||
|
| `@DisplayName` | 테스트 클래스나 메서드에 사용자 정의 이름을 지정 |
|
||||||
|
| `@ParameterizedTest` | 동일한 테스트를 여러 입력값으로 반복 실행 |
|
||||||
|
| `@ValueSource` | `@ParameterizedTest`와 함께 사용되며, 단일 값의 배열을 제공 |
|
||||||
|
| `@CsvSource` | `@ParameterizedTest`와 함께 사용되며, CSV 형식의 데이터를 제공 |
|
||||||
|
| `@MethodSource` | `@ParameterizedTest`와 함께 사용되며, 메서드에서 제공되는 데이터를 사용 |
|
||||||
|
| `@RepeatedTest` | 테스트를 지정된 횟수만큼 반복 실행 |
|
||||||
|
| `@Tag` | 테스트에 태그를 지정하여 필터링 가능 |
|
||||||
|
| `@Nested` | 테스트 클래스 내에 중첩된 테스트 클래스를 정의 |
|
||||||
|
| `@Timeout` | 테스트 실행 시간이 지정된 시간을 초과하면 실패 처리 |
|
||||||
|
| `@ExtendWith` | 사용자 정의 확장을 등록하여 테스트 동작을 커스터마이징 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 각 어노테이션 상세 설명 및 예시
|
||||||
|
|
||||||
|
#### 프로젝트 설정
|
||||||
|
JUnit 5를 사용하려면 `pom.xml`에 다음 의존성을 추가해야 합니다:
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>5.10.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1. `@Test`
|
||||||
|
- **설명**: 단위 테스트 메서드를 정의합니다. 별도의 설정 없이 실행됩니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class CalculatorTest {
|
||||||
|
@Test
|
||||||
|
void testAddition() {
|
||||||
|
Calculator calc = new Calculator();
|
||||||
|
assertEquals(4, calc.add(2, 2), "2 + 2는 4여야 합니다.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Calculator {
|
||||||
|
int add(int a, int b) { return a + b; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. `@BeforeAll`
|
||||||
|
- **설명**: 모든 테스트 전에 한 번만 실행되며, static 메서드여야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class DatabaseTest {
|
||||||
|
@BeforeAll
|
||||||
|
static void initDatabase() {
|
||||||
|
System.out.println("데이터베이스 초기화");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testQuery() {
|
||||||
|
System.out.println("쿼리 테스트");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: "데이터베이스 초기화" -> "쿼리 테스트"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. `@AfterAll`
|
||||||
|
- **설명**: 모든 테스트 후에 한 번만 실행되며, static 메서드여야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class DatabaseTest {
|
||||||
|
@AfterAll
|
||||||
|
static void closeDatabase() {
|
||||||
|
System.out.println("데이터베이스 종료");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testQuery() {
|
||||||
|
System.out.println("쿼리 테스트");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: "쿼리 테스트" -> "데이터베이스 종료"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. `@BeforeEach`
|
||||||
|
- **설명**: 각 테스트 메서드 실행 전에 실행됩니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class UserTest {
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
user = new User("홍길동");
|
||||||
|
System.out.println("유저 설정");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testName() {
|
||||||
|
System.out.println("이름 테스트: " + user.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: "유저 설정" -> "이름 테스트: 홍길동"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. `@AfterEach`
|
||||||
|
- **설명**: 각 테스트 메서드 실행 후에 실행됩니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class UserTest {
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() {
|
||||||
|
user = null;
|
||||||
|
System.out.println("유저 정리");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testName() {
|
||||||
|
user = new User("김영희");
|
||||||
|
System.out.println("이름 테스트");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: "이름 테스트" -> "유저 정리"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. `@Disabled`
|
||||||
|
- **설명**: 테스트를 비활성화하여 실행되지 않게 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class FeatureTest {
|
||||||
|
@Disabled("아직 구현되지 않음")
|
||||||
|
@Test
|
||||||
|
void testFeature() {
|
||||||
|
System.out.println("이 테스트는 실행되지 않습니다.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: 없음
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 7. `@DisplayName`
|
||||||
|
- **설명**: 테스트에 사용자 친화적인 이름을 붙입니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class MathTest {
|
||||||
|
@Test
|
||||||
|
@DisplayName("덧셈 테스트")
|
||||||
|
void testAdd() {
|
||||||
|
assertEquals(5, 2 + 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 테스트 결과에 "덧셈 테스트"로 표시됨
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 8. `@ParameterizedTest`
|
||||||
|
- **설명**: 동일한 테스트를 여러 입력값으로 실행합니다.
|
||||||
|
- **예시** (with `@ValueSource`):
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
public class NumberTest {
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(ints = {2, 4, 6})
|
||||||
|
void testEvenNumbers(int number) {
|
||||||
|
assertTrue(number % 2 == 0, number + "는 짝수여야 합니다.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2, 4, 6 각각에 대해 테스트 실행
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 9. `@CsvSource`
|
||||||
|
- **설명**: CSV 형식의 데이터를 제공하여 여러 파라미터로 테스트합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class MathTest {
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({"1, 2, 3", "4, 5, 9"})
|
||||||
|
void testAddition(int a, int b, int expected) {
|
||||||
|
assertEquals(expected, a + b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// (1, 2, 3), (4, 5, 9)로 테스트 실행
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 10. `@MethodSource`
|
||||||
|
- **설명**: 사용자 정의 메서드에서 테스트 데이터를 제공합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class StringTest {
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideStrings")
|
||||||
|
void testLength(String input, int expected) {
|
||||||
|
assertEquals(expected, input.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Arguments> provideStrings() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of("hello", 5),
|
||||||
|
Arguments.of("world", 5)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 11. `@RepeatedTest`
|
||||||
|
- **설명**: 테스트를 지정된 횟수만큼 반복 실행합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.RepeatedTest;
|
||||||
|
|
||||||
|
public class RandomTest {
|
||||||
|
@RepeatedTest(3)
|
||||||
|
void testRandom() {
|
||||||
|
System.out.println("랜덤 테스트: " + Math.random());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 출력: 3번 실행, 각각 다른 랜덤 값
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 12. `@Tag`
|
||||||
|
- **설명**: 테스트에 태그를 지정하여 특정 테스트만 실행할 수 있습니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class TaggedTest {
|
||||||
|
@Test
|
||||||
|
@Tag("fast")
|
||||||
|
void testFast() {
|
||||||
|
System.out.println("빠른 테스트");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Tag("slow")
|
||||||
|
void testSlow() {
|
||||||
|
System.out.println("느린 테스트");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// IDE나 빌드 도구에서 "fast" 태그만 실행 가능
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 13. `@Nested`
|
||||||
|
- **설명**: 테스트 클래스 내에 중첩된 테스트를 정의하여 계층적 테스트를 구성합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class OuterTest {
|
||||||
|
@Test
|
||||||
|
void outerTest() {
|
||||||
|
System.out.println("외부 테스트");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class InnerTest {
|
||||||
|
@Test
|
||||||
|
void innerTest() {
|
||||||
|
System.out.println("내부 테스트");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 14. `@Timeout`
|
||||||
|
- **설명**: 테스트가 지정된 시간을 초과하면 실패 처리합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.Timeout;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class TimeoutTest {
|
||||||
|
@Test
|
||||||
|
@Timeout(value = 1, unit = TimeUnit.SECONDS)
|
||||||
|
void testTimeout() throws InterruptedException {
|
||||||
|
Thread.sleep(2000); // 2초 대기 -> 실패
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 15. `@ExtendWith`
|
||||||
|
- **설명**: 사용자 정의 확장을 등록하여 테스트 동작을 확장합니다.
|
||||||
|
- **예시** (간단한 확장 예시):
|
||||||
|
```java
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
|
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
|
||||||
|
|
||||||
|
@ExtendWith(CustomExtension.class)
|
||||||
|
public class ExtensionTest {
|
||||||
|
@Test
|
||||||
|
void testWithExtension() {
|
||||||
|
System.out.println("확장 테스트");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomExtension implements TestExecutionExceptionHandler {
|
||||||
|
@Override
|
||||||
|
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) {
|
||||||
|
System.out.println("예외 처리: " + throwable.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 결론
|
||||||
|
JUnit 5는 다양한 어노테이션을 통해 테스트의 생명주기 관리, 반복 실행, 데이터 기반 테스트, 태깅 등 강력한 기능을 제공합니다. 이를 활용하면 코드 품질을 높이고, 유지보수성을 개선할 수 있습니다. 특히 `@ParameterizedTest`와 `@Nested` 같은 기능은 복잡한 테스트 시나리오를 체계적으로 관리하는 데 유용합니다. 프로젝트에서 JUnit을 사용할 때는 팀원 모두가 어노테이션의 역할과 사용법을 이해하고, 지속적인 통합(CI) 환경에서 테스트를 자동화하는 것이 좋습니다.
|
||||||
|
|
||||||
|
추가 질문이 있다면 언제든 물어보세요!
|
||||||
267
docs/lombok.md
Normal file
267
docs/lombok.md
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
자바의 Lombok에 대해 설명하는 글을 작성하겠습니다. Lombok은 자바에서 반복적인 코드를 줄여주는 라이브러리로, 어노테이션을 활용해 Boilerplate 코드(예: Getter, Setter, 생성자 등)를 자동으로 생성해줍니다. 이를 통해 코드 가독성을 높이고 개발 생산성을 향상시킬 수 있습니다.
|
||||||
|
|
||||||
|
먼저, Lombok에서 제공하는 주요 어노테이션을 표 형식으로 정리한 뒤, 각 어노테이션에 대해 설명과 예시를 상세히 다루겠습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Lombok 주요 어노테이션 표
|
||||||
|
|
||||||
|
| 어노테이션 | 설명 |
|
||||||
|
|-----------------------|----------------------------------------------------------------------|
|
||||||
|
| `@Getter` | 필드에 대해 Getter 메서드를 자동 생성 |
|
||||||
|
| `@Setter` | 필드에 대해 Setter 메서드를 자동 생성 |
|
||||||
|
| `@ToString` | 클래스의 toString() 메서드를 자동 생성 |
|
||||||
|
| `@EqualsAndHashCode` | equals()와 hashCode() 메서드를 자동 생성 |
|
||||||
|
| `@NoArgsConstructor` | 매개변수가 없는 기본 생성자를 자동 생성 |
|
||||||
|
| `@AllArgsConstructor`| 모든 필드를 매개변수로 받는 생성자를 자동 생성 |
|
||||||
|
| `@RequiredArgsConstructor` | `@NonNull`이 붙은 필드나 final 필드만 매개변수로 받는 생성자를 생성 |
|
||||||
|
| `@Data` | `@Getter`, `@Setter`, `@ToString`, `@EqualsAndHashCode`, `@RequiredArgsConstructor`를 포함 |
|
||||||
|
| `@Builder` | 빌더 패턴을 구현한 코드를 자동 생성 |
|
||||||
|
| `@Slf4j` | SLF4J 로깅 프레임워크를 위한 Logger 객체를 자동 생성 |
|
||||||
|
| `@NonNull` | 필드나 매개변수가 null이 될 수 없음을 명시 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 각 어노테이션 상세 설명 및 예시
|
||||||
|
|
||||||
|
#### 1. `@Getter`
|
||||||
|
- **설명**: 각 필드에 대해 Getter 메서드를 자동으로 생성합니다. 클래스의 모든 필드에 적용하려면 클래스 레벨에 붙일 수도 있습니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class Person {
|
||||||
|
private String name = "홍길동";
|
||||||
|
private int age = 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Person person = new Person();
|
||||||
|
System.out.println(person.getName()); // "홍길동" 출력
|
||||||
|
System.out.println(person.getAge()); // 30 출력
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. `@Setter`
|
||||||
|
- **설명**: 각 필드에 대해 Setter 메서드를 생성합니다. 클래스 레벨에서도 사용 가능하며, 특정 필드에만 적용하려면 필드 위에 붙입니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
public class Person {
|
||||||
|
private String name;
|
||||||
|
private int age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Person person = new Person();
|
||||||
|
person.setName("김영희");
|
||||||
|
person.setAge(25);
|
||||||
|
System.out.println(person); // toString 없으므로 객체 참조 출력
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. `@ToString`
|
||||||
|
- **설명**: 클래스의 필드를 기반으로 `toString()` 메서드를 생성합니다. 기본적으로 모든 필드를 포함하며, 제외하고 싶은 필드는 `@ToString.Exclude`를 사용합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
public class Person {
|
||||||
|
private String name = "이철수";
|
||||||
|
private int age = 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Person person = new Person();
|
||||||
|
System.out.println(person); // Person(name=이철수, age=40) 출력
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. `@EqualsAndHashCode`
|
||||||
|
- **설명**: `equals()`와 `hashCode()` 메서드를 필드 기반으로 생성합니다. 특정 필드를 제외하려면 `@EqualsAndHashCode.Exclude`를 사용합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class Person {
|
||||||
|
private String name = "박민수";
|
||||||
|
private int age = 28;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Person p1 = new Person();
|
||||||
|
Person p2 = new Person();
|
||||||
|
System.out.println(p1.equals(p2)); // true 출력
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. `@NoArgsConstructor`
|
||||||
|
- **설명**: 매개변수가 없는 기본 생성자를 생성합니다. 주로 JPA나 프레임워크에서 요구될 때 사용됩니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Person {
|
||||||
|
private String name = "최수진";
|
||||||
|
private int age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Person person = new Person(); // 기본 생성자 호출
|
||||||
|
System.out.println(person.name); // "최수진" 출력
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. `@AllArgsConstructor`
|
||||||
|
- **설명**: 모든 필드를 매개변수로 받는 생성자를 생성합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Person {
|
||||||
|
private String name;
|
||||||
|
private int age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Person person = new Person("김지영", 33);
|
||||||
|
System.out.println(person.name); // "김지영" 출력
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 7. `@RequiredArgsConstructor`
|
||||||
|
- **설명**: `@NonNull`이 붙은 필드나 `final` 필드만 포함한 생성자를 생성합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.NonNull;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class Person {
|
||||||
|
@NonNull private String name;
|
||||||
|
private int age; // 포함되지 않음
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Person person = new Person("윤서진"); // name만 요구됨
|
||||||
|
System.out.println(person.name); // "윤서진" 출력
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 8. `@Data`
|
||||||
|
- **설명**: `@Getter`, `@Setter`, `@ToString`, `@EqualsAndHashCode`, `@RequiredArgsConstructor`를 모두 포함하는 편리한 어노테이션입니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Person {
|
||||||
|
private String name;
|
||||||
|
private int age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Person person = new Person();
|
||||||
|
person.setName("이정훈");
|
||||||
|
person.setAge(29);
|
||||||
|
System.out.println(person); // Person(name=이정훈, age=29) 출력
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 9. `@Builder`
|
||||||
|
- **설명**: 빌더 패턴을 구현한 코드를 생성합니다. 객체를 유연하게 생성할 때 유용합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import lombok.Builder;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public class Person {
|
||||||
|
private String name;
|
||||||
|
private int age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Person person = Person.builder()
|
||||||
|
.name("최영미")
|
||||||
|
.age(35)
|
||||||
|
.build();
|
||||||
|
System.out.println(person.name); // "최영미" 출력
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 10. `@Slf4j`
|
||||||
|
- **설명**: SLF4J 기반의 Logger 객체를 자동 생성합니다. 로깅 코드를 간소화할 수 있습니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class Person {
|
||||||
|
private String name = "김태양";
|
||||||
|
|
||||||
|
public void printName() {
|
||||||
|
log.info("Name: {}", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Person person = new Person();
|
||||||
|
person.printName(); // INFO: Name: 김태양 출력
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 11. `@NonNull`
|
||||||
|
- **설명**: 필드나 매개변수가 null이 될 수 없음을 명시하며, null 체크 코드를 생성합니다. 주로 `@RequiredArgsConstructor`와 함께 사용됩니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import lombok.NonNull;
|
||||||
|
|
||||||
|
public class Person {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public Person(@NonNull String name) {
|
||||||
|
this.name = name; // null이면 예외 발생
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Person person = new Person("박준영"); // 정상 동작
|
||||||
|
// Person nullPerson = new Person(null); // NullPointerException 발생
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 결론
|
||||||
|
Lombok은 자바 개발에서 반복적인 코드를 줄이고, 가독성과 유지보수성을 높이는 데 큰 도움을 줍니다. 위 어노테이션들을 적절히 활용하면 생산성을 크게 향상시킬 수 있습니다. 단, 프로젝트에서 Lombok을 사용할 때는 팀원 모두가 이를 이해하고 IDE 설정(플러그인 설치 등)이 제대로 되어 있어야 원활한 협업이 가능합니다.
|
||||||
|
|
||||||
|
궁금한 점이 더 있다면 언제든 물어보세요!
|
||||||
391
docs/validation.md
Normal file
391
docs/validation.md
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
스프링 부트의 Validation에 대해 설명하는 글을 작성하겠습니다. 스프링 부트에서 Validation은 주로 Bean Validation API(Jakarta Validation, 이전 Hibernate Validator 기반)를 활용하여 요청 데이터의 유효성을 검사하는 데 사용됩니다. 이를 통해 입력값이 요구사항을 충족하는지 확인하고, 잘못된 데이터가 애플리케이션 로직에 들어가는 것을 방지할 수 있습니다. 스프링 부트는 `@Valid`와 같은 어노테이션을 통해 이를 컨트롤러에서 쉽게 적용할 수 있게 지원합니다.
|
||||||
|
|
||||||
|
아래에서는 Validation에 사용되는 주요 어노테이션을 표로 정리한 뒤, 각 어노테이션에 대한 설명과 예시를 상세히 다루겠습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 스프링 부트 Validation 주요 어노테이션 표
|
||||||
|
|
||||||
|
| 어노테이션 | 설명 |
|
||||||
|
|-----------------------|----------------------------------------------------------------------------------------|
|
||||||
|
| `@NotNull` | 값이 null이 아니어야 함 |
|
||||||
|
| `@NotBlank` | 문자열이 null이 아니고, 공백이 아닌 문자를 포함해야 함 |
|
||||||
|
| `@NotEmpty` | 컬렉션, 배열, 문자열 등이 null이거나 비어 있지 않아야 함 |
|
||||||
|
| `@Size` | 문자열, 컬렉션, 배열 등의 크기가 지정된 범위 내에 있어야 함 |
|
||||||
|
| `@Min` | 숫자가 지정된 최소값 이상이어야 함 |
|
||||||
|
| `@Max` | 숫자가 지정된 최대값 이하여야 함 |
|
||||||
|
| `@Pattern` | 값이 지정된 정규 표현식에 맞아야 함 |
|
||||||
|
| `@Email` | 문자열이 유효한 이메일 형식인지 확인 |
|
||||||
|
| `@Positive` | 숫자가 0보다 커야 함 |
|
||||||
|
| `@PositiveOrZero` | 숫자가 0 이상이어야 함 |
|
||||||
|
| `@Negative` | 숫자가 0보다 작아야 함 |
|
||||||
|
| `@NegativeOrZero` | 숫자가 0 이하여야 함 |
|
||||||
|
| `@Future` | 날짜가 현재보다 미래여야 함 |
|
||||||
|
| `@FutureOrPresent` | 날짜가 현재 또는 미래여야 함 |
|
||||||
|
| `@Past` | 날짜가 현재보다 과거여야 함 |
|
||||||
|
| `@PastOrPresent` | 날짜가 현재 또는 과거여야 함 |
|
||||||
|
| `@AssertTrue` | 값이 true여야 함 (논리 검증용) |
|
||||||
|
| `@AssertFalse` | 값이 false여야 함 (논리 검증용) |
|
||||||
|
| `@Digits` | 숫자가 지정된 자릿수(정수, 소수) 범위 내에 있어야 함 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 각 어노테이션 상세 설명 및 예시
|
||||||
|
|
||||||
|
#### 프로젝트 설정
|
||||||
|
예시를 실행하려면 `pom.xml`에 다음 의존성을 추가해야 합니다:
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1. `@NotNull`
|
||||||
|
- **설명**: 필드가 null이 아니어야 합니다. 공백 문자열("")은 허용됩니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@NotNull(message = "이름은 null일 수 없습니다.")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class UserController {
|
||||||
|
@PostMapping("/user")
|
||||||
|
public ResponseEntity<String> createUser(@Valid @RequestBody User user) {
|
||||||
|
return ResponseEntity.ok("유저 생성 성공");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 요청: {"name": null} -> 오류: "이름은 null일 수 없습니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. `@NotBlank`
|
||||||
|
- **설명**: 문자열이 null이 아니고, 공백이 아닌 문자를 포함해야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@NotBlank(message = "이름은 비어 있을 수 없습니다.")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
}
|
||||||
|
// 요청: {"name": ""} -> 오류: "이름은 비어 있을 수 없습니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. `@NotEmpty`
|
||||||
|
- **설명**: 컬렉션, 배열, 문자열 등이 null이거나 비어 있지 않아야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@NotEmpty(message = "취미 목록은 비어 있을 수 없습니다.")
|
||||||
|
private List<String> hobbies;
|
||||||
|
|
||||||
|
public List<String> getHobbies() { return hobbies; }
|
||||||
|
public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; }
|
||||||
|
}
|
||||||
|
// 요청: {"hobbies": []} -> 오류: "취미 목록은 비어 있을 수 없습니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. `@Size`
|
||||||
|
- **설명**: 문자열, 컬렉션 등의 크기가 지정된 범위 내에 있어야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@Size(min = 2, max = 10, message = "이름은 2~10자여야 합니다.")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
}
|
||||||
|
// 요청: {"name": "A"} -> 오류: "이름은 2~10자여야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. `@Min`
|
||||||
|
- **설명**: 숫자가 지정된 최소값 이상이어야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@Min(value = 18, message = "나이는 18 이상이어야 합니다.")
|
||||||
|
private int age;
|
||||||
|
|
||||||
|
public int getAge() { return age; }
|
||||||
|
public void setAge(int age) { this.age = age; }
|
||||||
|
}
|
||||||
|
// 요청: {"age": 15} -> 오류: "나이는 18 이상이어야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. `@Max`
|
||||||
|
- **설명**: 숫자가 지정된 최대값 이하여야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.Max;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@Max(value = 100, message = "나이는 100 이하여야 합니다.")
|
||||||
|
private int age;
|
||||||
|
|
||||||
|
public int getAge() { return age; }
|
||||||
|
public void setAge(int age) { this.age = age; }
|
||||||
|
}
|
||||||
|
// 요청: {"age": 150} -> 오류: "나이는 100 이하여야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 7. `@Pattern`
|
||||||
|
- **설명**: 값이 지정된 정규 표현식과 일치해야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "아이디는 영문자와 숫자만 가능합니다.")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
public String getUsername() { return username; }
|
||||||
|
public void setUsername(String username) { this.username = username; }
|
||||||
|
}
|
||||||
|
// 요청: {"username": "user#"} -> 오류: "아이디는 영문자와 숫자만 가능합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 8. `@Email`
|
||||||
|
- **설명**: 문자열이 유효한 이메일 형식이어야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.Email;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@Email(message = "유효한 이메일 형식이어야 합니다.")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
public String getEmail() { return email; }
|
||||||
|
public void setEmail(String email) { this.email = email; }
|
||||||
|
}
|
||||||
|
// 요청: {"email": "invalid-email"} -> 오류: "유효한 이메일 형식이어야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 9. `@Positive`
|
||||||
|
- **설명**: 숫자가 0보다 커야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.Positive;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@Positive(message = "나이는 양수여야 합니다.")
|
||||||
|
private int age;
|
||||||
|
|
||||||
|
public int getAge() { return age; }
|
||||||
|
public void setAge(int age) { this.age = age; }
|
||||||
|
}
|
||||||
|
// 요청: {"age": -5} -> 오류: "나이는 양수여야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 10. `@PositiveOrZero`
|
||||||
|
- **설명**: 숫자가 0 이상이어야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.PositiveOrZero;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@PositiveOrZero(message = "나이는 0 이상이어야 합니다.")
|
||||||
|
private int age;
|
||||||
|
|
||||||
|
public int getAge() { return age; }
|
||||||
|
public void setAge(int age) { this.age = age; }
|
||||||
|
}
|
||||||
|
// 요청: {"age": -1} -> 오류: "나이는 0 이상이어야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 11. `@Negative`
|
||||||
|
- **설명**: 숫자가 0보다 작아야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.Negative;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@Negative(message = "값은 음수여야 합니다.")
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
public int getValue() { return value; }
|
||||||
|
public void setValue(int value) { this.value = value; }
|
||||||
|
}
|
||||||
|
// 요청: {"value": 5} -> 오류: "값은 음수여야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 12. `@NegativeOrZero`
|
||||||
|
- **설명**: 숫자가 0 이하여야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.NegativeOrZero;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@NegativeOrZero(message = "값은 0 이하여야 합니다.")
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
public int getValue() { return value; }
|
||||||
|
public void setValue(int value) { this.value = value; }
|
||||||
|
}
|
||||||
|
// 요청: {"value": 1} -> 오류: "값은 0 이하여야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 13. `@Future`
|
||||||
|
- **설명**: 날짜가 현재보다 미래여야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.Future;
|
||||||
|
|
||||||
|
public class Event {
|
||||||
|
@Future(message = "이벤트 날짜는 미래여야 합니다.")
|
||||||
|
private LocalDate eventDate;
|
||||||
|
|
||||||
|
public LocalDate getEventDate() { return eventDate; }
|
||||||
|
public void setEventDate(LocalDate eventDate) { this.eventDate = eventDate; }
|
||||||
|
}
|
||||||
|
// 요청: {"eventDate": "2023-01-01"} -> 오류: "이벤트 날짜는 미래여야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 14. `@FutureOrPresent`
|
||||||
|
- **설명**: 날짜가 현재 또는 미래여야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.FutureOrPresent;
|
||||||
|
|
||||||
|
public class Event {
|
||||||
|
@FutureOrPresent(message = "이벤트 날짜는 현재 또는 미래여야 합니다.")
|
||||||
|
private LocalDate eventDate;
|
||||||
|
|
||||||
|
public LocalDate getEventDate() { return eventDate; }
|
||||||
|
public void setEventDate(LocalDate eventDate) { this.eventDate = eventDate; }
|
||||||
|
}
|
||||||
|
// 요청: {"eventDate": "2023-01-01"} -> 오류: "이벤트 날짜는 현재 또는 미래여야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 15. `@Past`
|
||||||
|
- **설명**: 날짜가 현재보다 과거여야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.Past;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@Past(message = "생일은 과거여야 합니다.")
|
||||||
|
private LocalDate birthDate;
|
||||||
|
|
||||||
|
public LocalDate getBirthDate() { return birthDate; }
|
||||||
|
public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; }
|
||||||
|
}
|
||||||
|
// 요청: {"birthDate": "2026-01-01"} -> 오류: "생일은 과거여야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 16. `@PastOrPresent`
|
||||||
|
- **설명**: 날짜가 현재 또는 과거여야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.PastOrPresent;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
@PastOrPresent(message = "생일은 현재 또는 과거여야 합니다.")
|
||||||
|
private LocalDate birthDate;
|
||||||
|
|
||||||
|
public LocalDate getBirthDate() { return birthDate; }
|
||||||
|
public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; }
|
||||||
|
}
|
||||||
|
// 요청: {"birthDate": "2026-01-01"} -> 오류: "생일은 현재 또는 과거여야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 17. `@AssertTrue`
|
||||||
|
- **설명**: 값이 true여야 합니다. 논리 검증에 유용합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.AssertTrue;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
private boolean active;
|
||||||
|
|
||||||
|
@AssertTrue(message = "사용자는 활성화 상태여야 합니다.")
|
||||||
|
public boolean isActive() { return active; }
|
||||||
|
|
||||||
|
public void setActive(boolean active) { this.active = active; }
|
||||||
|
}
|
||||||
|
// 요청: {"active": false} -> 오류: "사용자는 활성화 상태여야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 18. `@AssertFalse`
|
||||||
|
- **설명**: 값이 false여야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.AssertFalse;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
private boolean deleted;
|
||||||
|
|
||||||
|
@AssertFalse(message = "사용자는 삭제 상태일 수 없습니다.")
|
||||||
|
public boolean isDeleted() { return deleted; }
|
||||||
|
|
||||||
|
public void setDeleted(boolean deleted) { this.deleted = deleted; }
|
||||||
|
}
|
||||||
|
// 요청: {"deleted": true} -> 오류: "사용자는 삭제 상태일 수 없습니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 19. `@Digits`
|
||||||
|
- **설명**: 숫자의 정수 및 소수 자릿수가 지정된 범위 내에 있어야 합니다.
|
||||||
|
- **예시**:
|
||||||
|
```java
|
||||||
|
import jakarta.validation.constraints.Digits;
|
||||||
|
|
||||||
|
public class Product {
|
||||||
|
@Digits(integer = 3, fraction = 2, message = "가격은 정수 3자리, 소수 2자리여야 합니다.")
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
public BigDecimal getPrice() { return price; }
|
||||||
|
public void setPrice(BigDecimal price) { this.price = price; }
|
||||||
|
}
|
||||||
|
// 요청: {"price": 1234.567} -> 오류: "가격은 정수 3자리, 소수 2자리여야 합니다."
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 오류 처리 예시
|
||||||
|
Validation 오류를 처리하려면 `BindingResult`를 사용하거나, 전역 예외 처리를 설정할 수 있습니다:
|
||||||
|
```java
|
||||||
|
@RestController
|
||||||
|
public class UserController {
|
||||||
|
@PostMapping("/user")
|
||||||
|
public ResponseEntity<String> createUser(@Valid @RequestBody User user, BindingResult result) {
|
||||||
|
if (result.hasErrors()) {
|
||||||
|
return ResponseEntity.badRequest().body(result.getAllErrors().get(0).getDefaultMessage());
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok("유저 생성 성공");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 전역 예외 처리
|
||||||
|
```java
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public ResponseEntity<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
|
||||||
|
return ResponseEntity.badRequest().body(ex.getBindingResult().getAllErrors().get(0).getDefaultMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 결론
|
||||||
|
스프링 부트의 Validation은 데이터 유효성 검사를 간편하고 체계적으로 수행할 수 있게 해줍니다. 위 어노테이션들을 활용하면 숫자, 문자열, 날짜 등 다양한 데이터 타입에 대해 유연한 검증 규칙을 적용할 수 있습니다. 프로젝트에서는 요구사항에 맞는 어노테이션을 선택하고, 오류 메시지를 사용자 친화적으로 커스터마이징하여 더 나은 사용자 경험을 제공할 수 있습니다.
|
||||||
|
|
||||||
|
추가 질문이 있다면 언제든 물어보세요!
|
||||||
Reference in New Issue
Block a user