* 해당 글은 백엔드 자바 강의이후 회고 글입니다.
https://bootcamp.likelion.net/school/kdt-backendj-21th
백엔드 부트캠프 21기: Java : 멋사 부트캠프
실전 스킬 기반 백엔드 개발자 취업 완벽 대비 교육
bootcamp.likelion.net
어노테이션
어노테이션은 주석과 비슷한 역할을 담당한다고 할 수 있다.
주석은 사람에게 정보를 제공해주는 것이라면, 어노테이션은 다른 프로그램에게 유용한 정보를 제공하기 위해 사용된다.
- 코드에 선언하는 메타데이터이다.
- 실행 로직이 아니라 설정, 규칙, 정보등을 표현하는 주석이다.
- 컴파일러에게 문법 에러를 체크하도록 정보를 제공할 수 있다.
- 프로그램을 빌드할 때 코드를 자동으로 생성할 수 있도록 정보를 제공한다.
- 런타임에 특정 기능을 실행하도록 정보를 제공한다.
어노테이션의 종류
어노테이션은 크게 3가지로 구분된다. 자바에서 기본적으로 제공해주는 표준 어노테이션, 어노테이션을 정의하는데 사용되는 메타 어노테이션, 사용자 어노테이션 등이다.
표준 어노테이션 (자바에서 기본적으로 제공하는 어노테이션)
- @Override 어노테이션
- 컴파일러에게 부모 클래스로부터 오버라이딩한 것임을 알린다.
- 재정의된 메서드를 검증하기 위한 정보를 컴파일러에게 제공해준다.(오타, 문법 오류방지 등)
- @Deprecated 어노테이션
- 앞으로 사용하지 않을 대상임을 컴파일러에게 알린다.
- 사용중단 경고형 표시 -> 클래스, 생성자, 메서드, 필드등에 적용될 수 있다.
- @FunctionalInterface 어노테이션
- 함수형 인터페이스라는 것을 컴파일러에게 알린다.
- 추상 메서드 하나만 재정의 시킨다. 인터페이스에 적용되며 람다식 전용으로 사용된다.
@FunctionalInterface
interface My{
void Test();
}
- @SuppressWarning 어노테이션
- 컴파일러가 경고 메세지를 나타내지 않는다.(컴파일 경고 무시)
- 클래스, 생성자, 메서드, 필드 등에 적용될 수 있다.
@SuppressWarnings("unused")
@SuppressWarnings("unchecked") // 제네릭 경고 무시
- @SafeVaragrs 어노테이션
- 제네릭과 같은 가변 인자의 매개변수를 사용할 때 경고를 나타내지 않는다.
- 생성자, static, final 메서드 등에 적용될 수 있다.
메타 어노테이션(어노테이션에 대한 어노테이션)
- @Target 어노테이션
- 어노테이션을 정의할 때 어노테이션의 적용 대상을 지정하기 위해 사용된다.
// 생성자와 메서드에 어노테이션 적용
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
- @Documented 어노테이션
- 어노테이션 정보를 javadoc 으로 작성된 문서에 포함시킨다.
- @Inherited 어노테이션
- 어노테이션이 하위 클래스에 상속되도록 한다.
- @Retention 어노테이션
- 어노테이션을 정의할 때 적용 대상을 지정하는데 사용한다.
- SOURCE : 소스코드 까지만 어노테이션 정보 유지(컴파일 과정에서 어노테이션 정보 사라짐)
- CLASS : 클래스 파일까지만 어노테이션 정보 유지(런타임 시 유지 안됨)
- 배포를 위한 jar 파일과 같이 소스코드가 포함되어 있지않은 경우 클래스 파일까지 어노테이션 정보를 유지가 필요할 때 사용될 수 있다.
- RUNTIME : 런타임 시점까지 어노테이션 정보 유지(Reflection API 로 정보조회 가능)
- 런타임에 어노테이션 정보를 추출하여 사용할 수 있다.(Reflection API)
- 스프링의 경우 @Controller, @Service, @Autowired 어노테이션 내부에 @Retention(RetentionPolicy.RUNTIME) 과 같은 어노테이션이 포함되어 있는데, 이를 통해 스프링이 동작하는 실행 시점에 컴포넌트 스캔으로 컨트롤러, 서비스, 의존성 주입 객체등을 스프링 빈 객체들을 스프링 컨테이너에 적재할 수 있게된다
- @Repeatable 어노테이션
- 어노테이션을 반복해서 적용할 수 있도록 한다.
사용자 어노테이션(커스텀 어노테이션)
메타 데이터를 정의하기 위한 문법이며, 실제 동작은 리플렉션을 통해 구현된다.
참조 : https://ittrue.tistory.com/156
[Java] 어노테이션(Annotation) 개념 정리 및 종류
Intro 프로그래밍에서 주석은 개발자의 입장에서 더 직관적이고 코드를 이해하기 쉽게 하며, 다른 사람에게 설명할 수 있도록 정보를 제공하는 역할이다. 어노테이션 또한 주석과 비슷한 역할을
ittrue.tistory.com
어노테이션에 대한 리플렉션
리플렉션을 통해 클래스에 필드, 메서드 등에 어노테이션이 있는지 또한 확인할 수 있다.
아래의 코드를 한번 살펴보자.
import static java.lang.annotation.ElementType.METHOD;
@Retention(RUNTIME)
@Target(METHOD)
public @interface MyAnnotation {
}
class Test {
@MyAnnotation
public static void Prn() {
System.out.println("Test' Prn()");
}
public static void Prn02(){
System.out.println("Test' Prn02()");
}
}
// 임의의 클래스를 받아서 해당 어노테이션이 있는지 확인 후 해당 메서드 실행
// isAnnotationPresent() : 해당 객체에 어노테이션 선언 유무
public class c_reflect {
public static void main(String[] args){
Class<?> clazz = Test.class;
for(Method method : clazz.getDeclaredMethods()){
// 해당 메서드에 어노테이션이 있다면
if(method.isAnnotationPresent(MyAnnotation.class)){
System.out.println("어노테이션이 적용된 메서드 : " + method.getName());
} else {
System.out.println("어노테이션이 적용되지 않은 메서드 : " + method.getName());
}
}
}
}
- 위의 코드는 Test 클래스에 임의로 만든 사용자 어노테이션인 @MyAnnotation 이 적용되어 있는 메서드가 있는지 여부를 확인하는 코드이다.
- getDeclaredMethods() 메서드를 통해 클래스에 정의된 메서드 정보들을 가져온 다음, 각 메서드별로 isAnnotationPresent(MyAnnotation.class) 메서드를 통해 각 메서드들에 @MyAnnotation 어노테이션이 적용되어 있는지 여부를 확인한다.
메서드 뿐만이 아닌 생성자에도 리플렉션을 통해 어노테이션이 적용되어 있는지 확인해볼 수 있다.
아래의 코드는 MVC 패턴이 만들어졌다는 가정하에 MyController 클래스 내부에 정의되어 있는 메서드와 생성자에 대해 리플렉션을 통하여 특정한 어노테이션이 적용되어 있는지 여부를 확인하고 있다.
import java.lang.annotation.*;
// 1. 어노테이션 정의 (반드시 RUNTIME 으로 설정)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
@interface MyApi {
String path();
String method() default "GET";
}
// 2. 클래스 적용
class MyController {
@MyApi(path = "/users", method = "POST")
public void createUser(){
}
@MyApi(path = "/hello")
public void sayHello(){
}
@MyApi(path = "/my_member", method = "PUT")
public void myMember(){
}
@MyApi(path = "/myController", method = "Constructor")
public MyController(){
}
}
public class e_reflect {
public static void main(String[] args){
// 3.리플렉션으로 읽기
// MyController 클래스의 선언된 메서드를 리턴
Method[] methods = MyController.class.getDeclaredMethods();
for(Method m : methods) { // 메서드 하나씩 리턴
// 메서드에 MyApi.class 라는 어노테이션이 걸려있다면
if(m.isAnnotationPresent(MyApi.class)) {
// 어노테이션이 걸려있는 메서드를 해당 어노테이션 객체로 리턴
MyApi api = m.getAnnotation(MyApi.class);
System.out.println("메서드 명 : " + m.getName());
// 어노테이션이 걸려있는 각 메서드의 path 값 출력
System.out.println("경로 : " + api.path());
// 어노테이션이 걸려있는 각 메서드의 method 값 출력
System.out.println("경로 : " + api.method());
}
}
System.out.println("=====================================");
// 생성자 리플렉션
Constructor[] constructor = MyController.class.getDeclaredConstructors();
for(Constructor con : constructor) {
if(con.isAnnotationPresent(MyApi.class)){
MyApi api = (MyApi) con.getAnnotation(MyApi.class);
System.out.println("생성자 명 : " + con.getName());
System.out.println("경로 : " + api.path());
System.out.println("방식 : " + api.method());
}
}
}
}
이외에 알아두면 좋은것들
- 클래스 객체는 다음과 같이 리플렉션을 통해 동적으로 생성 가능하다.
// 컴파일 시점에 클래스 타입을 몰라도 > 를 통해 객체를 생성 가능하다.
// Spring IoC, Factory 패턴의 핵심적인 원리이다.
Class<?> clazz = Class.forName("com.test.Person");
Object obj = clazz.getDeclaredConstructor().newInstance();
- 리플렉션을 이용해 메서드를 동적으로 호출할 수 있다.
// 실행 시점에 메서드 호출, 메서드 이름을 문자열로 처리
Method method = clazz.getDeclaredMethod("print");
method.invoke(obj);
- 리플렉션은 어노테이션 처리와 함께 사용될 수 있다.
if(clazz.isAnnotationPresent(Service.class)){
// 객체 생성대상
}
Enum
Enum 은 프로젝트 상 필요한 상수의 집합을 하나의 타입(Type) 으로 정의하는 것이다.
- java.lang.Enum<E> : 모든 enum 은 자동으로 Enum 클래스를 상속한다. 그러므로 선언 시 extends Enum 으로 직접 쓸 수 없다.
- 값의 범위를 컴파일 시점에 고정시킨다.
- 정수형, 문자열의 상수 집합으로 구현한다.
- 필드, private 생성자, getter 로 멤버를 지정할 수 있다.
- 주의 : int, double 등의 상수의 경우엔 Enum 이 아닌 static final 을 통해 상수화 시키는것이 좋다.
- 한번 Enum 을 통해 상수값이 고정되면 오타를 수정하기 힘들뿐더러 int, double 과 같은 타입을 Enum 타입의 데이터로 지정하려고 할 경우 단순 데이터 값으로 지정되기 때문에 의미가 불명확해질 수 있다.
- 또한 값 하나만 대입하기 때문에 구체적인 범위 제한이 불가능하다.
- enum 은 클래스이면서 동시에 제한된 상수 타입이다.
- 일반 클래스가 상속 받을 수 있다.
아래의 서로 다른 Enum 클래스들을 보자.
- MyHttpMethod.java
// 필드 + 생성자 + 메서드
public enum MyHttpMethod {
GET("조회");
POST("등록");
PUT("수정");
DELETE("삭제");
MYENUM("내꺼");
// 필드 선언
private final String desc;
MyHttpMethod(String desc) {
this.desc = desc;
}
public String getDesc(){
return desc;
}
}
- MyHttpMethod02.java
// 문자열 나열형 상수
// 전략 패턴을 사용한 핵심 메서드를 switch - case 없이 추상 메서드로 재정의 하도록 한다.
public enum MyHttpMethod02 {
ADMIN("관리자") {
@Override
public String handle(String path, Object... args){
return "관리자 페이지 이동 : " + path + "-" + getDesc();
}
},
GET("조회") {
@Override
public String handle(String path, Object... args){
return "GET 처리 : " + path + "-" + getDesc();
}
},
POST("등록") {
@Override
public String handle(String path, Object... args){
return "POST 처리 : " + path + "-" + getDesc();
}
},
PUT("수정") {
@Override
public String handle(String path, Object... args){
return "PUT 처리 : " + path + "-" + getDesc();
}
},
DELETE("삭제") {
@Override
public String handle(String path, Object... args){
return "DELETE 처리 : " + path + "-" + getDesc();
}
},
MYENUM("내꺼") {
@Override
public String handle(String path, Object... args){
return "MYENUM 처리 : " + path + "-" + getDesc();
}
};
// 필드 선언
private final String desc;
// 생성자
private MyHttpMethod02(String desc){
this.desc = desc;
}
// 공통멤버 메서드
public String getDesc(){
return desc;
}
// 추상 메서드 추가 : 전략 메서드 기능
// (핵심) switch - case 없이 enum 상수별로 기능 구현하도록 선언
public abstract String handle(String path, Object... args);
}
위의 두 Enum 클래스를 활용하는 코드를 작성해보자.
public class d_enum {
public static void MyHandler(MyHttpMethod method){
String result = switch(method) {
case GET -> "GET 처리" + method.getDesc();
case POST -> "POST 처리" + method.getDesc();
case PUT -> "PUT 처리" + method.getDesc();
case DELETE -> "DELETE 처리" + method.getDesc();
case MYENUM -> "사용자 처리" + method.getDesc();
}
}
// MyHttpMethod02 에서 Enum 타입 상수들을
// 전략타입을 활용하여 만든 추상 메서드 handle을 각 상수에 맞게끔 재정의 하는 방식으로
// MyHttpMethod 와는 달리 switch - case 없이 상수별로 기능을 구현했다.
public static void main(String[] args){
System.out.println(MyHttpMethod02.ADMIN.handle("/admin"));
System.out.println(MyHttpMethod02.GET.handle("/member"));
System.out.println(MyHttpMethod02.DELETE.handle("/users/10"));
System.out.println(MyHttpMethod02.MYENUM.handle("/custom"));
System.out.println(MyHttpMethod02.POST.handle("/users", "홍길동"));
}
}'부트캠프 > 후기 챌린지' 카테고리의 다른 글
| [멋쟁이사자처럼부트캠프] 백엔드 자바 21기 (2026.01.26) #1 (0) | 2026.03.25 |
|---|---|
| [멋쟁이사자처럼부트캠프] 백엔드 자바 21기 (2026.01.22) (0) | 2026.03.23 |
| [멋쟁이사자처럼부트캠프] 백엔드 자바 21기 (2026.01.21) (0) | 2026.03.09 |
| [멋쟁이사자처럼부트캠프] 백엔드 자바 21기 (2026.01.20) (1) | 2026.02.27 |
| [멋쟁이사자처럼부트캠프] 백엔드 자바 21기 (2026.01.19) (0) | 2026.02.09 |