블로그
전체 42024. 02. 24.
0
[인프런 워밍업 스터디 클럽 0기 Back] API 작성 5일차 미션
[문제] [답]import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.Scanner; public class Main { // "는" 이 붙는 숫자에 대한 값들 -> 2는 4는 5는 private static final List VALID_NUMBERS = Arrays.asList(2, 4, 5); public static void main(String[] args) { // Scanner의 close를 위해 try 구문으로 작성 try (Scanner scanner = new Scanner(System.in)) { // 주사위 범위 정하기 int diceNuber = getDiceNumber(scanner); // 주사위 던지는 횟수 구하기 int rollsNumber = getRollsNumber(scanner); // 주사위 횟수대로 던진 결과값을 저장 int[] resultArray = rollDice(diceNuber, rollsNumber); // 결과를 출력 printAllResults(resultArray); } } public static int getDiceNumber(Scanner scanner) { System.out.print("주사위 눈의 숫자를 입력하세요 : "); return scanner.nextInt(); } public static int getRollsNumber(Scanner scanner){ System.out.print("숫자를 입력하세요 : "); return scanner.nextInt(); } public static int[] rollDice(int diceNuber, int rollsNumber) { int[] array = new int[diceNuber]; Random random = new Random(); // Random 객체 생성 for (int i = 0; i 기존 코드의 기능을 크게 나누면던지는 횟수를 입력받는 부분.주사위 범위를 초기화 하는 부분.주사위를 던지는 과정.해당 결과를 출력하는 과정. 답에 대한 부분int diceNuber = getDiceNumber(scanner);해당 코드를 작성하여 주사위의 범위를 지정했다. 기존 코드에서는 6으로 하드코딩 되어있었지만,주사위의 범위가 변경될 가능성도 있기 때문에 확장성을 추가하기 위해 해당 메서드를 구현했다. int rollsNumber = getRollsNumber(scanner);해당 코드를 작성하여 주사위를 던지는 횟수를 지정했다. 기존 코드의 숫자를 입력하세요. 와 변수 a에 그 값을 담는 부분에 대한 기능을 수행한다. int[] resultArray = rollDice(diceNuber, rollsNumber);입력받은 주사위의 범위와 던진 횟수를 매개변수로 주사위를 굴리는 행위를 구현했다.결과값은 배열이며 int 자료형으로 구현. printAllResults(resultArray);결과값을 반복해서 출력하기 위한 출력작업을 수행한다.
백엔드
2024. 02. 21.
0
[인프런 워밍업 스터디 클럽 0기 Back] API 작성 3일차 미션
익명 클래스정의 및 쓰임이름 없이 선언과 동시에 객체를 생성하는 클래스를 뜻한다.익명 클래스는 일회용으로 사용되며, 인터페이스나 추상 클래스를 간편하게 구현할 때 사용된다.따라서, 익명 클래스는 단 한 번만 사용되고 다시는 재사용되지 않는 기능을 구현할 때 유용하다.사용하는 방법선언과 객체 생성익명 클래스는 선언과 객체 생성이 동시에 이뤄진다.new 키워드 뒤에 상속 받는 클래스나 구현할 인터페이스 이름이 온다.그 뒤에 클래스 본문이 중괄호 안에 정의된다.구현익명 클래스는 상속ㄱ받은 클래스나 구현한 인터페이스의 메서드를 오버라이드(재정의)하여 상요할 수 있다.익명 클래스 내부에서만 사용되는 로컬 변수에 접근할 때는 해당 변수가 final 인 경우에만 접근이 가능하다.사용 범위익명 클래스는 선언된 위치에서만 사용할 수 있다.코드의 가독성을 높이고 코드를 간결하게 만드는데 도움을 준다.package com.example.springlibraryapp.controller.calculator; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class AnonymousClassExample { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new Runnable() { @Override public void run() { System.out.println("비동기 작업 실행"); } }); future.get(); System.out.println("비동기 작업 완료"); executor.shutdown(); } } 람다의의 및 효과간결한 방식으로 익명 메서드를 표현할 수 있다.코드의 길이를 줄이고, 가독성을 향상시킨다.람다식은 주로 함수형 인터페이스의 인스턴스를 생성할 때 사용된다.함수형 인터페이스란 추상 메서드를 단 하나만 가지고 있는 인터페이스이다.람다식의 기본 문법매개변수 목록메서드 매개변수 목록을 나타내며, 괄호 안에 정의된다.매개변수의 타입을 명시할 수 있지만, 컴파일러가 문맥을 통해 유추할 수 있기 때문에 생략할 수 있다.화살표 : 이것을 통해 매개변수와 실행문을 구분한다.실행문람다식의 본체이며, 실제 수행될 코드를 나타낸다.단일 실행문만 있을 경우 중괄호를 생략할 수 있고, 실행 결과가 자동으로 반환된다.실행문이 둘 이상이거나, 반환값이 있으면 중괄호를 써야한다.(매개변수 목록) -> {실행문} 람다식 예시() -> System.out.println("Hello, Lambda"); name -> Systeom.out.println("Hello, " + name); (int a, int b) -> {return a + b;}; (String s) -> {return s.toUpperCase();}; List list = Arrays.asList("apple", "banana", "cherry"); list.forEach(element -> System.out.println(element)); 함수형 프로그래밍프로그래밍의 패러다임이며, 계산을 수학적 함수 평가로 간주하고 상태 변경이나 가변 데이터를 피하는 스타일이다.특징불변성데이터가 한 번 생성되면 변경되지 않는다.함수형 프로그래밍에서는 데이터를 변경하는 대신, 변경된 데이터의 새로운 복사본을 생성해서 사용한다.함수의 일급 객체함수를 변수에 할당하거나 다른 함수의 인자로 전달하고, 함수에서 함수를 반환할 수 있다.고차 함수다른 함수를 인자로 받거나 함수를 결과로 반환하는 함수고차 함수를 통해 코드 재사용성과 추상화 수준을 높일 수 있다.순수 함수동일한 인자에 대해 항상 동일한 결과를 반환한다.외부 상태를 변경하지 않고 외부 상태에 의존하지 않는 함수이다.순수 함수의 사용은 프로그램의 안정성을 높이고, 테스트와 디버깅을 용이하게 한다.레이지 평가필요할 때까지 계산을 지연시키는 기법불필요한 계산을 줄이고, 성능 최적화에 도움을 준다.함수 조합여러 함수를 조합해서 새로운 함수를 만드는 기법.작은 함수 여러 개를 조합해서 복잡한 기능을 구현할 수 있다.자바에서 함수형 프로그래밍을 사용하는 이유간결한 코드 : 람다 표현식과 메서드 참조를 사용하면 코드가 더 간결하고 명확해진다.병렬 처리 용이성스트림 API를 사용하면 데이터 컬렉션의 병렬처리가 용이해진다.멀티코어 프로세서의 이점을 쉽게 활용할 수 있다.높은 수준의 추상화함수형 인터페이스와 고차 함수를 통해 코드의 추상화 수준을 높일 수 있다.코드의 재사용성과 모듈성을 향상시킨다.@FunctionalInterface의의해당 인터페이스가 함수형 인터페이스임을 명시하는 어노테이션.함수형 인터페이스는 추상 메서드를 딱 하나만 가지고 있는 인터페이스이며, 람다 표현식과 함께 사용될 수 있다.따라서, 컴파일러에게 해당 인터페이스가 함수형 인터페이스의 조건을 충족하는지 검증 요청을 하며, 조건을 충족하지 못하면 컴파일 오류를 발생시킨다.함수형 인터페이스의 조건정확히 하나의 추상화 메서드를 가져야한다.추상 메서드는 람다 표현식의 시그니처(메서드 이름, 입력 매개변수, 반환 타입)을 정의한다.default 메서드나 static 메서드는 여러 개 있어도 된다.이것들은 구현이 제공되므로 추상 메서드 조건에 영향을 주지 않는다.java.lang.Object 클래스의 public 메서드는 추상 메서드의 개수에 포함되지 않는다.목적명시성인터페이스가 함수형 인터페이스임을 분명하게 한다.따라서, 다른 개발자들이 이를 쉽게 이해하고 사용할 수 있게 한다.안정성컴파일 시점에 인터페이스가 함수형 인터페이스의 조건을 만족하는지 검증한다.따라서, 실수로 추가된 추상 메서드로 인한 오류를 방지한다.유지 보수성프로젝트의 후반부나 다른 개발자가 코드를 수정할 때, 해당 인터페이스가 함수형 인터페이스로 설계되어있음을 알린다.예제추상메서드 execute 하나만 가지고 있으므로 함수형 인터페이스이다.@FunctionalInterface 어노테이션을 붙임으로써, 이 인터페이스가 의도적으로 함수형 인터페이스로 설계되었음을 명시한다.이후 추상 메서드를 추가하려고 하면 컴파일러는 오류를 발생시킨다.따라서, 해당 어노테이션을 붙여 함수형 인터페이스의 정의를 위반하는 것을 방지함.@FunctionalInterface public interface SimpleFunctionalInterface { void execute(); // 추상 메소드 default void print(String text) { System.out.println(text); } static void staticMethod() { System.out.println("인터페이스 내의 정적 메소드"); } } Stream API의의와 효과컬렉션, 배열, I/O 자원 등의 데이터를 함수형 스타일로 처리할 수 있게 하는 기능이다.데이터를 선언적으로 처리할 수 있으며, 멀티스레딩이나 동시성 문제에 대해 직접적으로 걱정하지 않고도 데이터의 병렬 처리가 가능하다.코드의 가독성과 작성의 용이성을 크게 향상시킨다.스트림 API의 특징불변셩 : 데이터를 변경하지 않는다. 대신, 각 연산은 결과를 포함한 새로운 스트림을 반환한다.내부 반복 : 컬렉션의 반복을 추상화하여, 반복 방식을 스트림 APi가 내부적으로 처리한다.연산의 체이닝 : 체인으로 연결할 수 있으며, 더 복잡한 표현식 구성을 가능하게 한다.지연 실행 : 터미널 연산이 호출될 때까지 실행되지 않는다. → 성능 최적화에 도움병렬 처리 지원 : 데이터를 자동으로 분할하여 병렬 처리할 수 있는 기능을 제공한다.주요 연산중간 연산(intermediate operations)스트림을 변환하는 연산으로, 하나 이상의 중간 연산을 연결할 수 있으며, 중간 연산은 게으른 방식으로 실행된다.filter(Predicate) : 조건에 맞는 요소만을 포함하는 새로운 스트림을 반환한다.map(Function) : 각 요소를 주어진 함수를 사용하여 변환하고 변환된 요소를 포함하는 새 스트림을 반환한다.sorted() : 요소를 자연 순서 또는 Comparator에 따라 정렬한 새 스트립을 반환한다.터미널 연산(terminal operations)스트림을 소비하여 결과를 도출하는 연산. 스트림 파이프라인을 실행하고 종료한다.forEach(Consumer) : 스트림의 각 요소에 대해 지정된 작업을 수행한다.collect(Collector) : 스트림의 요소를 특정 결과 컨테이너에 수집한다.reduce(BinaryOperator) : 스트림의 요소를 조합하여 요약 결과를 생성한다.스트림API 사용 예시map 중간 연산을 사용하여 해당 리스트의 이름을 대문자로 변환하고, collect 터미널 연산을 통해 결과를 새로운 리스트로 수집한다.import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List names = Arrays.asList("John", "Jane", "Mary", "Adam", "Diana"); List upperCaseNames = names.stream() .map(String::toUpperCase) .collect(Collectors.toList()); System.out.println(upperCaseNames); } } 결론스트림 API는 데이터 처리 작업을 더 쉽고 간결하게 만들어주는 강력한 도구이다.병렬 처리를 통한 성능 최적화뿐만 아니라, 코드의 가독성과 유지 보수성 측면에서도 많은 이점을 제공한다.메서드 레퍼런스의의메서드를 직접 호출하는 대신 사용할 수 있는 구문이다.메서드 레퍼런스를 사용하면 이미 존재하는 메서드나 생성자를 간결하게 참조할 수 있다.람다 표현식을 더 단순화시킬 수 있게 하며, 코드 가독성을 향상 시킨다.기본 구문정적 메서드 참조클래스의 정적 메서드를 참조한다.ClassName::methodNameex, String::valueOf, String.valueOf(…)특정 개체의 인스턴스 메서드 참조특정 객체의 인스턴스 메서드를 참조한다.instance::methodeNameex, System.out::println, System.outPrintln(…)임의 개체의 인스턴스 메서드 참조특정 유형의 모든 개체에 대한 인스턴스 메서드를 참조한다.ClassName::methodNameString::toLowerCase 모든 String 객체에 대한 toLowerCase() 메서드의 참조생성자 참조클래스의 생성자를 참조한다.ClassName::newArrayList::new, ArrayList사용 예시public class Main { public static void main(String[] args) { // 정적 메서드 참조 Function parseInt = Integer::parseInt;; Integer number = parseInt.apply("100"); System.out.println(number); // 특정 개체의 인스턴스 메서드 참조 List names = new ArrayList(); names.add("James"); names.add("Emily"); names.forEach(System.out::println); // 생성자 참조 Supplier> listSupplier = ArrayList::new; List nameList = listSupplier.get(); nameList.add("David"); nameList.forEach(System.out::println); } } 자바의 람다식은 왜 등장했을까?의의람다식을 통해 함수형 프로그래밍 패러다임을 도입했다.코드를 더 간결하고 유연하게 만들도록, 개발자들이 멀티코어 프로세싱과 병렬 처리를 더 쉽게 활용할 수 있도록 설계하였다.람다식의 등장 배경함수형 프로그래밍의 수요 증가함수형 프로그래밍 패러다임은 부작용 최소화, 코드의 가독성 향상, 병렬 처리와 비동기 처리 용이 등의 장점을 갖는다.이러한 패러다임 때문에 수요가 증가하고, 자바 또한 이것을 지원하기 위해 힘썼다.코드의 간결성과 가독성 향상익명 클래스를 사용하여 함수형 인터페이스를 구현하는 방식을 사용했다.이 방법은 코드가 길어지고 가독성이 떨어진다.람다식을 도입하여 코드를 더 간결하고 읽기 쉽게 만들게 되었다.병렬 처리 및 멀티코어 활용의 필요성현대 컴퓨터는 대부분 멀티코어 프로세서를 사용한다.멀티코어 프로세서의 효율적인 활용을 위해 병렬처리가 필수적이다.람다식과 스트림 API를 통해 자바는 병렬 처리를 더 쉽게 구현할 수 있게 되었다.따라서, 성능을 크게 향상시켰다.자바의 현대화다른 언어는 이미 함수형 프로그래밍 기능을 제공하고 있었다.자바 또한 현대 프로그래밍 트랜드에 발밪춰 람다식을 도입.현대적인 프로그래밍 요구사항을 충족시키기 위한 노력.람다식의 목적과 기능익명함수의 제공람다식을 통해 익명함수를 간단하게 표현할 수 있게 되었다.컬렉션의 처리, 이벤트 리스너, 비동기 처리에 유용하다.함수형 인터페이스의 간편한 구현람다식은 함수형 인터페이스의 인스턴스를 간단하게 생성하는 방법을 제공한다.인터페이스의 추상 메서드를 구현하는데 필요한 코드 양을 크게 줄일 수 있다.스트림 API와의 결합람다식은 스트림 API와 결합하여 컬렉션 처리를 위한 강력한 도구를 제공한다.데이터의 필터링, 변환, 집계 등을 간결하고 선언적으로 수행할 수 있다.병렬 처리의 용이성스트림 API와 람다식을 사용하여 컬렉션의 병렬처리를 간단하게 구현할 수 있다.멀티코어 프로세서의 이점을 최대한 활용하여, 애플리케이션의 성능을 향상시킬 수 있다.결론람다식 도임은 자바 프로그래밍 언어의 큰 변화이다.자바 개발자들에게 더 많은 표현력과 유연성을 제공한다.자바는 현대적인 프로그래밍 요구사항을 충족시켰다.계속해서 발전하는 프로그래밍 언어 생태계에서 중요한 위치를 차지하게 되었다.람다식과 익명 클래스는 어떤 관계가 있을까? - 람다식의 문법은 어떻게 될까?의의자바에서 함수적 인터페이스의 인스턴스를 생성하는 두 가지 방법이라는 공통점이 있다.함수적 인터페이스란, 하나의 추상 메서드를 가진 인터페이스이다.두 방법 모두 익명의 내부 클래스를 생성하는 방식이지만, 람다식은 함수형 프로그래밍 개념을 이용하여 익명 클래스보다 코드가 간결하다.서로의 관계익명 클래스자바 초기 버전부터 사용됐다.인터페이스나 추상 클래스의 추상 메서드를 구현할 때 사용된다.익명 클래스는 이름이 없으며, 직접 인스턴스화하여 사용한다.익명 클래스는 코드가 길어지고 가독성이 떨어진다.람다식자바 8에서 도입된 기능.익명 클래스를 대체하는 간결한 방법을 제공한다.주로 함수형 인터페이스의 구현체를 생성할 때 사용된다.람다식을 사용하면 보다 간결하고 가독성이 높다.람다식의 기본 문법매개변수 목록메서드 매개변수 목록을 나타내며, 괄호 안에 정의된다.매개변수의 타입을 명시할 수 있지만, 컴파일러가 문맥을 통해 유추할 수 있기 때문에 생략할 수 있다.화살표 : 이것을 통해 매개변수와 실행문을 구분한다.실행문람다식의 본체이며, 실제 수행될 코드를 나타낸다.단일 실행문만 있을 경우 중괄호를 생략할 수 있고, 실행 결과가 자동으로 반환된다.실행문이 둘 이상이거나, 반환값이 있으면 중괄호를 써야한다.(매개변수 목록) -> {실행문} () -> System.out.println("Hello, Lambda"); name -> Systeom.out.println("Hello, " + name); (int a, int b) -> {return a + b;}; (String s) -> {return s.toUpperCase();}; List list = Arrays.asList("apple", "banana", "cherry"); list.forEach(element -> System.out.println(element));
백엔드
2024. 02. 20.
0
[인프런 워밍업 스터디 클럽 0기 Back] API 작성 2일차 미션
package com.example.springlibraryapp.controller.calculator; import com.example.springlibraryapp.dto.calculator.request.CalculatorAddRequest; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import org.springframework.web.bind.annotation.*; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.format.TextStyle; import java.util.ArrayList; import java.util.List; import java.util.Locale; @RestController public class Controller { // 1번 문제 @GetMapping("/api/v1/calc") public calculateTwoNumbersResult calculateTwoNumbers(CalculatorAddRequest request) { int addResult = request.getNumber1() + request.getNumber2(); int minusResult = request.getNumber1() - request.getNumber2(); int multiplyResult = request.getNumber1() * request.getNumber2(); return new calculateTwoNumbersResult(addResult, minusResult, multiplyResult); } @Getter @RequiredArgsConstructor static class calculateTwoNumbersResult { private final int add; private final int minus; private final int multiply; } // 2번문제 @GetMapping("/api/v1/day-of-the-week") public DayOfTheWeekResponse getDayOfWeek(@RequestParam(value = "date") String dateString) { LocalDate date = LocalDate.parse(dateString); DayOfWeek dayOfWeek = date.getDayOfWeek(); String dayOfWeekUpperCase = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.ENGLISH).toUpperCase(); return new DayOfTheWeekResponse(dayOfWeekUpperCase); } @Getter @Setter @RequiredArgsConstructor static class DayOfTheWeekResponse { private final String dayOfTheWeek; } // 3번 문제 @PostMapping("/api/post") public int postSumArray(@RequestBody PostSumArrayRequest request) { List numbers = request.getNumbers(); int result = 0; for (Integer number : numbers) { result += number; } return result; } @Getter @Setter static class PostSumArrayRequest { private ArrayList numbers; } }
백엔드
2024. 02. 18.
0
[인프런 워밍업 스터디 클럽 0기 Back] 어노테이션 관련 1일차 미션
1일차 과제우리는 최초로 API를 만들어 보았습니다. GET API를 만들기 위해 사용했던 어노테이션에 익숙하지 않다면 자바 어노테이션에 대해서 몇 가지 블로그 글을 찾아보세요! 다음 질문을 생각하며 공부해보면 좋습니다! 😊[질문]어노테이션을 사용하는 이유 (효과) 는 무엇일까?나만의 어노테이션은 어떻게 만들 수 있을까?[답]어노테이션이란?코드에게 메타데이터를 제공하는 방법이다.어노테이션은 자바 코드에 직접 포함되지만, 어노테이션 자체는 실행 가능한 코드처럼 동작하지 않는다.어노테이션은 직접적으로 프로그램에 영향을 주는 것이 아니며, 메타 데이터를 제공하는 역할을 한다.어노테이션의 목적컴파일 시점에서의 검사 및 처리어노테이션을 통해 컴파일러는 코드의 정확성을 검사하고, 필요에 따라 코드를 자동으로 생성할 수 있다.정확성 검사@Ovrride 어노테이션을 사용하면, 메서드가 상위 클래스나 인터페이스의 메서드를 오버라이드 한다는 것을 명시한다.컴파일러는 이 어노테이션을 참고하여, 실제로 상위 클래스나 인터페이스에 해당 메서드가 있는지 검사한다.메서드 시그니처가 일치하지 않게 되면 컴파일 에러가 발생한다.코드 생성Lombok 같은 라이브러리는 @Getter 등의 어노테이션을 통해 컴파일때 게터를 자동으로 생성한다.반복적인 보일러플레이트 코드를 대폭 줄여준다.코드 분석 도구일부 어노테이션은 코드 분석 도구에 사용될 수 있다.@Deprecated 어노테이션이 붙은 사용되지 않는 메서드를 찾아 개발자에게 경고한다.런타임에서의 동작 제어런타임에서는 리플렉션 API를 사용하여 어노테이션 정보를 조회하고, 프로그램의 동작을 제어한다.조건부 로직 실행@Secured 어노테이션을 사용하면 메서드 실행 전 사용자 권한을 체크하는 로직이 실행된다.보안 관련 로직을 중앙에서 관리하고, 코드의 재사용성을 높이는데 도움을 준다.동적 구성@Autowired 어노테이션을 사용해 의존성을 자동으로 주입하면 런타임에 객체 간의 의존 관계가 동적으로 구성된다.설정 코드를 줄이고, 유연한 구성을 가능하게 만든다.개발 도구에서의 사용IDE나 빌드 도구는 어노테이션을 사용해 코드 구조를 이해하고, 개발자에게 유용한 정보를 제공하거나, 특정 작업을 자동화한다.IDE 지원어노테이션을 통해 코드의 문제점을 더 쉽게 식별할 수 있도록 돕는다.@NotNull 어노티에션을 변수로 적용하면, IDE는 해당 변수에 null 값이 할당되는 경우 경고를 표시한다.빌드 자동화Maven, Gradle 등 빌드 도구는 어노테이션 프로세서를 통해 컴파일 시점에 코드 생성 또는 변환 작업을 자동으로 수행한다.빌드 과정을 최적화하고, 반복적인 작업을 자동화하는데 도움이 된다.테스트 프레임워크 통합JUnit 같은 테스트 프레임워크는 @Test 어노테이션을 사용해 테스트 메서드를 식별하고 실행한다.따라서 테스트 코드의 구조를 명확하게 하고, 테스트 실행을 간편하게 할 수 있다.나만의 어노테이션package com.example.springlibraryapp; import jakarta.validation.Constraint; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; import jakarta.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) @Constraint(validatedBy = EmailValidator.class) public @interface ValidEmail { String message() default "올바른 emil 형식이 아닙니다."; Class[] groups() default {}; Class[] payload() default {}; } public class EmailValidator implements ConstraintValidator { @Override public void initialize(ValidEmail constraintAnnotation) {} @Override public boolean isValid(String email, ConstraintValidatorContext context) { if (email == null) { return false; } return email.matches("^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\\\\\\\\.[A-Za-z]{2,})$"); } } @ValidEmail 어노테이션@Retention(retentionPolicy.RUNTIME) : 런타임에도 유지된다는 것을 정의. 리플렉션을 통해 런타임에 어노티에션 정보를 조회할 수 있음을 의미한다.@Target({ElemintType.FIELD, ElementType.METHOD}) : 필드와 메서드에만 적용될 수 있음을 나타낸다.@Constraint(validatedBy = EmailValidator.class) : 유효성 검증 로직이 EmailValidator 클래스에 의해 정의된다는 것을 나타낸다.@interface : 어노테이션을 정의할 때 사용되는 키워드이다.String message() default : 유효성 검사를 통과하지 못할 경우 반환될 기본 메시지Class[] groups() default : 유효성 검사 그룹을 지정할 때 사용된다. 특정 그룹만 검사하도록 함.Class[] payload() default : 클라이언트에게 유효성 검사 실패에 대한 추가적인 정보를 제공하는 매커니즘을 지정할 때 사용된다.EmailValidator 클래스implements ConstraintValidator : ConstraintValidator 인터페이스를 구현한다. ValidEmail은 지원하는 어노테이션 타입, Stirng은 검증할 데이터의 타입public void initialize(ValidEmail constraintAnnotation) : 초기화시 호출되는 메서드.public boolean isValid(String email, ConstraintValidatorContext context)실제 유효성 검사 로직을 구현한다.입력된 email이 null이 아니고, 주어진 정규식에 맞는지 확인하여 결과를 반환한다.회고스프링을 사용할 때 반드시 사용되는 것들이 어노테이션이다. 스프링에서사용되는 어노테이션들은 단순히 사용법으로 인식을 했기 때문에 아무런 생각 없이 사용을 하고 있었는데, 생각해보면 어노테이션 자체가 무엇인지도, 해당 어노테이션을 사용한다고 어떻게 빈으로 등록되는지도 이유를 모르고 있었다.해당 과제를 통해 어노테이션 학습을 통해 메타 데이터를 스프링에 전달을 함으로써, 해당 클래스가 빈으로 등록되고 컨트롤러라는 것을 정의한다는 것은 알았지만, 어떻게해서 그렇게 되는 것인지는 방대한 스프링을 공부하면서 천천히 알아갈 예정이다.또한, 어노테이션은 이미 작성된 것 이외에, @interface를 통해 커스텀 어노테이션을 만들 수 있다는 사실을 알게되었다. 그 전에는 스프링에서 제공하거나, Lombok에서 제공하는 어노테이션을 기계적으로 사용했는데, 커스텀 어노테이션을 통해 내가 필요로 하는 어노테이션을 직접 작성하여 코드를 간결하게 만들 수 있다는 것이 의미있게 다가왔다. 물론 그것을 어떻게 활용할지는 미래의 나에게 달려있는 문제이지만....미션 해결과정ChatGPT를 많이 활용했다... 개발자의 자리를 위협할 가능성이 있는 도구지만, 개인적으로는 최고의 선생님이자 최고의 도구라고 생각한다.여튼, 나만의 어노테이션을 만들어야 한다는 미션이 주어졌고, 단순히 어노테이션을 정의하는 부분에서 끝낼 수 있긴 했지만, 개인적으로는 어짜피 시간을 투자하는거 나에게 의미가 있는 어노테이션을 만들자는 생각이 들었다. 어떠한 어노테이션을 만들까 고민을 했지만, 유효성 검증 이외에는 유의미한 응용 방법이 떠오르질 않았다. 유효성 검증에 대해 생각을 하니, 이메일 관련 유효성 검증 어노테이션을 만들면 좋을 것 같다는 생각으로 이어졌다.검색을 통해 어노테이션을 정의하는 방법에 대해 알게되었고, 어노테이션을 이용해 커스텀 어노테이션의 설정을 하는 방법을 알게 되었고, 비즈니스 로직은 클래스를 통해 위임된다는 것도 알게되었다. 따라서 검증을 하는 클래스를 만들고, 정규식은 인터넷을 참고하여 이용을 해서 해당 과제를 수행할 수 있었다.
백엔드