Spring Boot 3.0.4 환경에서 OAuth2-Client 라이브러리를 적용 후 소셜 로그인을 구현하면서 이슈 하나가 발생하였는데 어떤 이슈인지 문제 원인은 무엇이고 어떻게 해결해야 하는지 말씀드리겠습니다. 이슈 OAuth2-Client 라이브러리를 적용 후 Access Token을 조회하려면 application.yml 파일에 spring.security.oauth2.client.registration.[id].client-authentication-method 프로퍼티에 POST를 적어줘야 하는데 POST를 적어도 401 예외가 발생합니다. 원인 client-authentication-method 프로퍼티에 POST로 정의하면 해당 조건문이 false가 되어 Access Token 발급 API..
회사에서 소셜 로그인을 구현해야 할 업무가 생겼는데 마침 진행하고 있는 프로젝트의 Spring Boot 버전이 3.0.4여서 Spring Boot 3.x 버전에서 OAuth2 라이브러리를 이용하여 카카오 로그인을 구현하고 동작 원리를 살펴보겠습니다. 제가 정리한 내용을 순차적으로 진행하시면 문제없이 카카오 로그인을 구현하실 수 있습니다. Kakao Developers 설정 카카오 디벨로퍼 사이트에 접속하셔서 로그인하시고 우측상단에 보이는 '내 애플리케이션'을 클릭하고 애플리케이션을 생성합니다. 앱 설정 > 요약정보 페이지에 가시면 4가지의 앱 키가 있는데 REST API 키를 복사해 둡니다. 이 키가 서버에서 사용할 client-id 값이 됩니다. client-id 값은 밑에서 다시 설명하겠습니다. 앱 ..
Spring WebFlux로 API를 만드는 과정에서 이슈가 발생하였습니다. Flux 타입을 응답할 때 당연히 JSON Array로 직렬화되어서 응답하는 줄 알았는데 반드시 그런 건 아니었습니다. Flux 타입일 경우 JSON Array로 직렬화가 되지 않은 이슈를 겪었는데요. 코드로 함께 살펴보겠습니다. 예제코드 Flux와 Flux을 반환하는 두 개의 API가 있습니다. Flux를 반환하는 API에 요청을 보내면 Content-Type이 application/json, Body는 JSON Array로 응답합니다. 하지만 Flux을 반환하는 API에 요청을 보내면 Content-Type은 application/json이 아닌 text/plain으로 응답하고 Body 또한 JSON Array가 아니라 Ar..
얼마 전에 AWS SES + Spring Boot를 이용해서 이메일 발송하는 예제를 작성했었는데요. 이번에는 파일을 첨부하여 발송하는 기능까지 알아보겠습니다. 이미 AWS SES 공식 문서 맨 하단에 Java로 작성된 예제코드가 작성돼있는데요. 핵심 부분만 잠시 살펴보겠습니다. AWS SES 공식문서 예제코드 HTML 삽입 미리보기할 수 없는 소스 예제코드이지만 코드가 너무 길고 가독성이 좋지 못한데요. Spring Mail 라이브러리에서는 이러한 코드를 더욱 쉽게 사용할 수 있게 제공하는데 좀 더 자세히 알아보겠습니다. 예제코드 HTML 삽입 미리보기할 수 없는 소스 spring-boot-starter-mail, aws ses 두 라이브러리를 추가해줍니다. AWS SES 공식 문서에서 제공된 예제코드는..
클라이언트에서 전달받은 날짜 포맷의 데이터를 @JsonFormat을 이용하여 ZonedDateTime 타입의 변수에 바인딩할 때주의할 점에 대해 알아보겠습니다. 일단 클라이언트에서 전달받은 날짜 포맷의 데이터를 ZoneDateTime 타입으로 받을 때 UTC로 변환되는 이슈가 있었는데요. 예제코드 (1)을 통해 함께 알아보겠습니다. 예제코드 (1) ZonedDateTime 타입의 필드를 가진 Product 클래스가 있고 이 필드 위에는 다음과 같은 어노테이션이 선언돼있습니다. 위 예제에서의 @JsonFormat 역할은 "yyyy-MM-dd HH:mm:ss" 패턴의 String 데이터를 ZonedDateTime 타입으로 역직렬화를 해주는 코드이며 Timezone은 Asia/Seoul로 세팅하는 코드입니다...
저번 글에서는 Spring Boot와 Spring Cloud AWS 라이브러리를 이용하여 AWS SQS에 대해 간단히 알아보았습니다. 이번에는 Spring Cloud AWS SQS에서 제공해주는 두 가지 내용들에 대해 주의할 점에 대해 알아보겠습니다. 살펴볼 내용은 다음과 같습니다. 1. MappingJackson2MessageConverter 2. ThreadPoolTaskExecutor (@SqsListener Multiple 구성 시 주의할 점) 1. MappingJackson2MessageConverter Spring Cloud AWS SQS의 코어 기능들은 Spring Cloud AWS Messaging 라이브러리에 포함되어 있으며 Spring Cloud AWS Messaging 라이브러리 내부..
Spring Boot와 AWS SQS를 활용한 간단한 애플리케이션을 만들고자 합니다. 제목에 Spring Boot 2.4.x 이상 환경이라고 말씀드린 이유는 여기에서 확인해보실 수 있습니다. 대략 내용을 말씀드리자면 Spring Cloud AWS 2.3 버전이 출시되었는데 Spring Boot 2.4.x 이상 버전과 함께 사용할 수 있다고 합니다. 기존에 Spring Cloud AWS 관련 라이브러리를 사용할 때 org.springframework.cloud.aws 라이브러리를 사용했지만 지금은 업데이트가 중단되었습니다. Spring Boot 2.4.x 이상 환경에서 호환되는 Spring Cloud AWS 2.3에서는 org.springframework.cloud.aws 패키지에 있던 모든 클래스들이 i..
클래스 및 메서드에 Proxy 기반으로 동작하는 어노테이션을 선언해주면 해당 클래스의 메서드에 부가 기능이 추가되어 실행됩니다. 대표적으로 @Transactional이 있습니다. 이번 글에서는 Proxy 기반으로 동작하는 어노테이션을 AspectJ 모듈과 함께 사용할 경우 주의할 점에 대해 알아보겠습니다. 예제코드를 함께 살펴보겠습니다. 예제코드 위 코드는 DB에서 데이터를 조회한 후 JPA Dirty Checking으로 Entity의 상태를 변경하는 단순한 코드입니다. update 쿼리가 정상적으로 실행된 걸 확인할 수 있습니다. 이 쿼리가 실행되는 이유는 JPA의 영속성 컨텍스트의 스코프는 기본적으로 @Transactional이 선언된 메서드가 호출되어 종료될 때까지 유효합니다. 해당 Entity가 ..
Spring Configuration을 구성할 때 @Configuration 클래스 내부의 메서드에다가 @Bean을 선언하여 구성합니다. 만일 동일한 타입의 @Bean이 여러 개이고 환경별로 다르게 동작하게끔 구성하기 위해서는 @Profile을 사용해 처리할 수 있습니다. 그런데 이 @Profile을 사용할 때 주의해야 할 점이 있는데요. 예제코드와 함께 살펴보겠습니다. 예제코드 위 코드는 AWS SES를 이용하기 위해 AmazonSimpleEmailService 타입의 Bean을 생성하는 코드입니다. local 환경에서는 설정 파일에 있는 access-key, secret-key 정보로 credentials 정보를 생성하여 이용할 수 있도록, dev 환경은 EC2에 배포되니 IMDS를 이용하여 EC2에..
사용자에게 메일을 발송할 때 기존에는 Daum에서 제공하는 SMTP 서비스를 사용했었습니다. 근데 간헐적으로 메일발송이 되지 않거나 10~20분 후 발송되는 현상이 있어서 SMTP 서비스를 AWS SES로 바꾸기로 하였습니다. 그래서 이번 글에서는 AWS SES와 Spring Boot로 이메일 전송기능을 만드는 작업을 정리해보겠습니다. 위에서부터 순서대로 진행하시면 문제없이 만드실 수 있습니다. AWS SES 세팅 AWS 계정을 만들고 SES 서비스를 처음 이용하면 해당 계정은 샌드박스에 있습니다. 이 샌드박스에 있으면 다음과 같은 활동이 제한됩니다. - 자격증명된 이메일로만 메일을 보낼 수 있습니다. - 24시간 동안 최대 200개의 메세지를 보낼 수 있습니다. - 초당 최대 1개의 메세지를 보낼 수 ..
Spring 웹 애플리케이션에서 AWS SDK를 이용하여 AWS 리소스에 액세스하기 위해서는 accessKey와 secretKey로 AWS Credentials 자격증명을 관리하는 객체를 생성해서 액세스할 수 있습니다. 그런데 이러한 웹 애플리케이션이 EC2에 배포가 된다면 accessKey와 secretKey를 발급하지 않고도 IAM에 역할을 부여하고 인스턴스 메타데이터를 이용하여 액세스할 수 있는 방법도 있습니다. 그런데 인스턴스 메타데이터를 이용하여 AWS 리소스에 액세스할 경우 주의해야 할 점이 있는데요. 지금부터 천천히 알아보도록 하겠습니다. 애플리케이션 코드 HTML 삽입 미리보기할 수 없는 소스 위 코드는 인스턴스 메타데이터를 이용하여 EC2에 부여된 IAM 역할을 기반으로 credentia..
Spring Boot에 starter-test 라이브러리를 추가하면 JUnit API를 이용하여 테스트 코드를 작성할 수 있습니다. 테스트 코드를 작성하고 검증할 때 검증하고자 하는 값이 실행환경에 따라 동적으로 변경된다면 그 실행환경에 맞게끔 설정을 해줘야 합니다. Spring에서는 ActiveProfilesResolver 라는 인터페이스를 제공해주는데 이 인터페이스를 활용하여 처리할 수 있습니다. 간단히 예제코드를 보면서 내용을 살펴보도록 하겠습니다. 테스트 환경 Spring Boot Gradle 테스트 코드 위 코드는 ActiveProfiles 라는 어노테이션을 통해 테스트 코드를 실행할 Spring Profile을 지정해주고 해당 Profile에 맞는 Property를 조회해오는 코드입니다. 결과..
Spring으로 웹 애플리케이션 개발을 진행하다보면 이미지, 파일 등 정적 리소스를 저장할 저장소가 필요하게 된다. 가장 많이 쓰여지는 것 중 하나가 AWS에서 제공하는 S3가 있는데 이걸 이용하려면 credentials 인증을 어플리케이션에서 해줘야 한다. credentials 인증 방식은 많이 있지만 사람들이 제일 많이 쓰는 방법 중 하나가 application.properties 파일같은 설정파일에 accessKey와 secretAccessKey를 넣어서 사용하는데 이는 보안에 매우 취약하다. 만약 github에 accessKey, secretAccessKey를 그대로 올리기라도 하면 큰 피해를 입을 수 있으며 실제로 그런 사례가 많이 있다. 그래서 이번에는 accessKey와 secretAcces..
Spring에서 file, classpath에 위치한 리소스를 제공해주는 Resource 라는 추상화된 인터페이스를 제공해준다. 이번글에서는 Resource 인터페이스의 구현체인 ClassPathResource와 PathMatchingResourcePatternResolver를 이용하여 classpath에 위치한 resource를 가져오는 5가지의 방법을 알아보도록 한다. classpath resource 현재 classpath의 resource에는 다음과 같은 디렉토리 및 파일들이 존재한다. classpath resource 조회 ClassPathResource 생성자 또는 PathMatchingResourcePatternResolver 클래스의 getResource 메서드로 classpath에 위치한..
이번글에서는 Spring Boot에서 Database Replication이 구성된 AWS RDS쪽으로 Read와 Write의 부하를 분산하게끔 구성을 해보려고 한다. 그전에 Database Replication에 대해 간단한 개념을 짚고 가자. Database Replication Replication의 사전적 의미는 복제다. 그렇다면 Database Replication은 데이터베이스를 복제하는걸 말하는데 기준이 되는 서버를 Primary라 불리우며 복제된 서버는 Secondary라고 부른다. 기준이 되는 Primary는 1대로 구성되며 복제된 Secondary는 N대로 구성된다. Database Replication 구성하는 이유 트래픽이 급증할 경우 1대의 데이터베이스에 쓰기(insert, upd..
Spring Boot 프로젝트를 생성 후 .java파일을 제외한 애플리케이션 개발 시 필요한 리소스 파일을 src/main/resources 디렉토리 하위에 위치해두는데 대표적으로 application.properties(yml) 파일이 있다. 그리고 application.properties(yml) 파일에는 애플리케이션이 배포 될 환경별로 환경에 맞는 값을 세팅해두거나 (ex. DB정보) 로깅용 xml 파일을 세팅해두는데 이러한 리소스 파일들을 효율적으로 관리하는 방법에 대해 알아보자. 참고로 이 방법은 필자가 개인적으로 생각했을 때 효율적이라고 판단되서 작성하는 것.. 프로젝트 환경 Spring Boot Maven Gradle 예제 src/main/resources 디렉토리와 src/main/reso..
프로젝트 환경 JAVA 17 Spring Boot 2.6.2 Intellij 2021.1 내용 JAVA 버전을 11에서 17로 변경 후 애플리케이션을 실행하니 OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release 라는 경고메세지가 출력되었다. 경고메세지를 확인해보니 해당 옵션은 JDK 13이상부터는 더이상 사용되지 않으며 향후 릴리스에서 제거될 가능성이 있다고 하는데 애플리케이션이 정상적으로 실행이 되는거 보면 문제는 없겠지만 찝찝해서 지워야겠다는 생각을 했다. Edit Configurations를 클릭 ..
회사 소스코드 application.yml 설정파일을 보면 데이터베이스 접속정보, api키값 등 보안에 민감한 데이터가 있어서 이러한 데이터를 외부에서 직접 주입 할 수 있도록 구성하려고 이것 저것 알아보다가 AWS 서비스중 하나인 Parameter Store 에 대해 알게되었는데 비용도 무료고 괜찮아 보여서 적용해보도록 하였다. Spring Boot 환경에서 AWS Parameter Store를 적용하는 블로그 글들은 많이 봤지만 Spring Boot 2.4 이상 환경에서 적용하려고 했지만 제대로 동작하지는 않았다. 여기를 보면 Spring Cloud 버전과 호환되는 Spring Boot 버전을 확인할 수 있는데 버전을 맞춰도 안되는건 마찬가지 였다. 필자는 Spring Boot 버전이 2.6이어서 호..
Spring에서 Controller의 메서드에 예외가 발생되면 @ControllerAdvice가 선언된 클래스가 동작하여 발생된 예외타입에 맞는 메서드가 호출된다. 그리고 그 메서드 내부에는 개발자마다 다르겠지만 e.printStackTrace()를 호출하는 코드가 작성되있는 경우가 있다. 혹은 특정 로직을 try/catch로 감싸서 처리할 경우 catch 블록에도 e.printStackTrace()를 호출하는 코드가 작성되있는 경우가 있다 그러나 e.printStackTrace()는 특히 운영환경에서는 지양해야될 요소이며 이유는 다음과 같다. JAVA의 Reflection을 이용하여 예외를 추적하는 것이라 많은 오버헤드가 발생할 수 있다. 메서드 스택정보를 취합하기 때문에 서버 부하의 원인이기도 하다...
이번글에서는 Spring 애플리케이션에서 발생된 로그를 AWS Cloud Watch로 전송하는 방법에 대해 알아보자. 그전에 Spring 애플리케이션에서 AWS Cloud Watch로 로그를 전송하려면 CloudWatchLogsFullAccess 라는 Role을 가진 IAM 사용자의 액세스 키, 시크릿 키가 존재해야 한다. IAM 사용자를 추가하고 Role을 부여하는 방법은 여기를 참고하자. 프로젝트 환경 JAVA 11 Spring Boot 2.6.2 Maven AWS Log Appender 라이브러리 추가 Maven ca.pjer logback-awslogs-appender 1.6.0 Gradle implementation group: 'ca.pjer', name: 'logback-awslogs-app..
Spring에서는 @Component(@Controller, @Service, @Repository)을 이용하여 Bean을 생성할 수 있는데 이때 해당 클래스 내부의 메서드에 Proxy 기반으로 동작하는 어노테이션이 존재할 경우 target class 타입의 Bean이 아닌 Proxy로 래핑된 Bean이 생성된다. @Transactional, @Async, @Cacheable 등 이런 어노테이션들은 런타임 시 target class를 Proxy가 래핑하여 로직을 처리하는데 사용 시 주의할 점이 있다. 한번 살펴보자 Proxy Bean 생성 @Service 어노테이션으로 Bean을 생성 후 @Async 어노테이션으로 Future타입의 결과값을 반환하는 메서드를 선언하였다. 그리고 외부에서 접근이 가능한 S..
백오피스의 신규기능을 추가해달라는 운영팀의 요구사항이 들어와서 해당기능을 구현했다. 해당 기능은 상세페이지에서 해당 내용과 관련된 파일들을 S3에 업로드하는 기능이었는데 파일의 용량은 적었지만 갯수가 최대 15개까지 업로드가 되야했다. 문제는 파일을 업로드 하고 업로드된 파일을 서버에 전송할 때 서버에서 처리하는 시간이 2 ~ 2.5초 내외로 걸리다 보니 바로 응답이 오지 않아 운영팀에서는 로딩바를 추가해달라고 요청이 들어왔다. 하지만 나는 백엔드 개발자다보니 로딩바를 추가하는 것 보다 서버에서 처리속도를 개선하고 싶어서 어떻게 하면 빨리 처리될 수 있을까 고민을 하다가 비동기 방식의 멀티쓰레드를 이용해서 처리하면 아무래도 하나의 쓰레드로 모든 요청을 처리하는 것 보단 시간이 오래걸리는 작업들은 별도 쓰..
Spring Boot로 애플리케이션을 개발하다보면 데이터베이스 접속정보, AWS accessKey와 secretKey 등 중요한 정보는 application.properties(yml)에 값을 세팅하고 개발하게되는데 이런 정보들이 그대로 노출되면 위험하기 때문에 외부에서 애플리케이션을 실행할 때 넣어주는게 안전하다. 테스트할 코드 작성 해당코드는 application.properties에 secret-key라는 key값에 임의의 값을 세팅하고 Controller에서 @Value 어노테이션으로 해당값을 주입받아서 Get 요청을 보내면 그 값을 리턴한다. 이제 해당 값을 외부에서 주입해보자 Command line arguments application.properties에 secret-key라는 key값엔 ..
Spring의 Controller 클래스도 Spring Bean이고 Spring Bean은 기본적으로 싱글톤인데 어떻게 수많은 요청을 Thread-Safe하게 처리가 가능한지 궁금했다. 그런데 이 문제에 대한 답은 JAVA 메모리 구조를 공부했으면 금방 이해할 수 있을 것이다. @RestController public class UserController { @PostMapping("/users") public void save(User user) { ... } } 위 코드는 사용자의 정보를 저장하는 Controller의 save 메서드가 있다. 동시에 여러개의 요청이 들어오면 Tomcat은 내부적으로 다음과 같이 요청을 처리한다. 요청이 들어온 순서대로 Queue에 담는다. ThreadPool에 현재 ..
Spring으로 개발하면 @Controller, @Service, @Repository 등의 어노테이션으로 Bean을 생성할 수 있는데 그렇다면 Bean을 생성할 때 어떤 클래스를 Bean으로 생성해야하는 것 일까? 그건 바로 값들을 담는 클래스가 아니라 작업을 하는 클래스를 Bean으로 생성해야한다. 예를들어 유저의 정보를 담고 있는 User클래스가 있고 유저의 정보로 관련된 작업을 하는 UserController클래스가 있다고 할 때 UserController는 Bean으로 등록해야하고 User클래스는 Bean으로 등록하지 않는다. @RestController public class UserController { @PostMapping("/users") public void save(User user..
기존 Spring 애플리케이션 환경을 구축할 때 주로 XML파일을 기반으로 개발에 필요한 모든설정을 잡아줬다. 가령 Spring MVC로 개발환경을 구성할 때 servlet-context.xml과 root-context.xml에 세팅하는데 설정을 잘못해서 예외가 발생하면 디버깅이 힘들었고 구축하는데에도 많은시간이 걸렸다. 그리고 컨트롤러에서 View를 렌더링 하는게 아닌 JSON 데이터를 전달하려고 할 때 jackson 라이브러리를 디펜던시에 추가해줘야하고 프론트에서 텍스트데이터가 아닌 파일형식의 데이터를 받아줄 때도 Multipart 디펜던시도 추가해줘야 한다. 이처럼 개발할 때 필요한 디펜던시들을 개발자가 하나하나 직접 찾아가면서 Spring 프로젝트 버전에 호환되게 등록해줬어야 했고 이러한 개발자의..
Spring에서 제공하는 HandlerMethodArgumentResolver는 컨트롤러의 메서드에 조건에 맞는 파라미터 및 어노테이션이 정의되있다면 원하는 값을 주입해주는 인터페이스 입니다. 메서드 매개변수에 @RequestBody 어노테이션이 정의되었으면 HandlerMethodArgumentResolver의 구현체인 RequestResponseBodyMethodProcessor가 동작하여 request body에 있는 Json 포맷의 데이터를 Java 객체에 바인딩을 해줍니다. 세션을 이용하여 로그인 기능을 구현하게되면 로그인 시 입력된 값으로 DB에 사용자를 조회후 조회한 값을 세션에저장할 것 이고 사용자의 정보가 필요할 경우 HttpSession을 컨트로러 메서드 매개변수에 주입받아 사용자의 정..
Spring Cloud Aws 의존성이 추가후 애플리케이션을 실행하면 콘솔에 "Failed to connect to service endpoint"라는 예외메시지가 나오는데 원인과 해결방법에 대해 알아보자. Failed to connect to service endpoint 원인파악 해당예외 메세지가 왜 뜨는지 분석해보자 AwsCloudEnvironmentCheckUtils AwsCloudEnvironmentCheckUtils클래스에서 isRunningOnCloudEnvironment 메서드가 호출 된다. EC2MetadataUtils의 getData 메서드를 호출하는데 getData 메서드 내부를 까보면 getItems 메서드가 호출된다. EC2MetadataUtils getItems 메서드를 호출할 ..
이번장에는 Spring Boot와 AWS SDK를 이용하여 AWS S3에 이미지/파일 정적리소스들을 등록, 조회, 삭제하는 내용에 대해 정리해보고자한다. 그전에 AWS S3에 버킷생성 / IAM 사용자를 생성후 S3 액세스 권한을 부여해줘야한다. 해당작업을 시작하기전에 위 두가지가 완료되지 않았다면 아래링크를 통해 먼저준비해두자 S3버킷 및 객체 생성링크부분에서는 퍼블릭 액세스 접근설정만 하면된다. ('객체생성' 전 단계) https://kim-jong-hyun.tistory.com/84 2. AWS - S3 버킷 및 객체 생성 이번장에는 S3 버킷을 생성하고 버킷내부에 객체를 생성해보자. AWS 콘솔접속 AWS에 로그인하여 콘솔화면 검색창에 S3를 검색해서 들어간다. AWS 버킷 생성하기 화면우측에 있..
Spring Boot로 Bean을 생성시 Bean이 생성되지 않고 다음과 비슷 예외메세지가 본 적이 있을 것이다. NoSuchBeanDefinitionException: No qualifying bean of type 'com.kjh.component.StudyComponent' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} StudyComponent 타입의 Bean을 찾지못했다는 메세지인데 왜 이런 메세지가 나오는지 예제코드와 함꼐 확인해보자. ..