Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

엘라의 개발 스케치 Note

[TIL] 내일배움캠프 101일차(23.08.23.) - HTTPS, 브라우저 작동방식, 자바 스프링 알림 기능 구현 방법, java.time 패키지, 이메일 알림 기능 구현 트러블슈팅, CI 환경 구축 트러블슈팅 본문

내일배움캠프/TIL

[TIL] 내일배움캠프 101일차(23.08.23.) - HTTPS, 브라우저 작동방식, 자바 스프링 알림 기능 구현 방법, java.time 패키지, 이메일 알림 기능 구현 트러블슈팅, CI 환경 구축 트러블슈팅

엘라랑이 2023. 8. 24. 22:19

To-do

  • CS 스터디 + 기술면접 대비 공부
  • 최종 프로젝트 회의 -> 일정 및 진행상황 공유, 코드 리뷰
  • 최종 프로젝트 작성 -> 알림 기능 구현(이메일), CI 환경 구축 오류 해결
  • 알고리즘 스터디

 


TIL

< HTTPS >

  • HTTPS는 HyperText Transfer Protocol Secure의 약자로, 웹 브라우저와 웹 서버 간의 안전하고 암호화된 통신을 제공하는 프로토콜
더보기
  • 기본적으로 HTTP 프로토콜을 기반으로 하지만 데이터의 보안을 강화하기 위해 TLS(Transport Layer Security) 또는 SSL(Secure Sockets Layer) 프로토콜을 사용하여 데이터를 암호화
  • HTTPS의 주요 특징과 동작 방식
    • 데이터 암호화: HTTPS는 데이터를 암호화하여 전송하므로 중간에 제3자가 데이터를 가로채더라도 읽을 수 없음. 이로써 사용자의 민감한 정보(로그인 정보, 결제 정보 등)가 보호됨
    • 신뢰성 확보: HTTPS는 인증된 SSL/TLS 인증서를 사용하여 웹 사이트의 신원을 확인. 사용자는 이를 통해 해당 웹 사이트가 신뢰할 수 있는지 확인할 수 있음
    • SEO 영향: 구글과 같은 검색 엔진은 HTTPS 사용 여부를 검색 순위에 영향을 미치는 요소로 고려하므로, 웹사이트의 SEO(검색 엔진 최적화)에도 영향을 미침
    • 보안 경고: 모든 미디어 및 스크립트도 암호화되므로 브라우저는 미안전한 요소가 포함되어 있을 경우 경고를 표시하므로 사용자에게 안전한 경험을 제공
    • 인증서 발급: HTTPS 사용을 위해 SSL/TLS 인증서를 발급받아야 함. 이를 위해 CA(인증 기관)에서 인증서를 구입하거나 무료 인증서를 얻을 수 있음
  • HTTPS는 사용자의 개인정보 보호와 데이터의 안전성을 강화하여 웹 사이트와 사용자 간의 신뢰성을 확보. 이로써 웹 사이트는 보안적인 측면에서 더욱 신뢰할 수 있는 환경을 제공할 수 있음

 


 

< 브라우저의 작동방식 >

더보기

1. 사용자 입력 및 URL 해석:
사용자가 웹 브라우저에 URL을 입력하면, 브라우저는 해당 URL을 해석하여 서버의 IP 주소로 변환하고 요청할 리소스를 결정합니다.

2. 서버 요청 및 응답:
브라우저는 서버에 HTTP 요청을 보내 웹 페이지나 리소스를 요청합니다. 서버는 해당 요청에 대해 HTTP 응답을 반환하는데, 이 응답은 HTML, CSS, JavaScript, 이미지 등의 리소스를 포함할 수 있습니다.

3. HTML 파싱 및 렌더링 트리 구축:
브라우저는 받아온 HTML 문서를 파싱하여 DOM(Document Object Model) 트리로 변환합니다. 이후에 CSS 스타일 및 외부 리소스도 파싱되어 렌더링 트리가 구축됩니다.

4. 렌더링 및 레이아웃:
브라우저는 렌더링 트리를 기반으로 화면에 웹 페이지를 렌더링합니다. 레이아웃은 각 요소의 크기와 위치를 결정하는 단계로, CSS 스타일과 DOM 구조에 따라 화면에 배치됩니다.

5. 화면 표시 및 인터랙션:
최종적으로 웹 페이지가 사용자의 화면에 표시되며, 사용자가 페이지와 상호작용할 수 있습니다. JavaScript를 통해 동적으로 콘텐츠를 변경하거나 이벤트를 처리할 수 있습니다.

6. 외부 리소스 로딩:
웹 페이지에는 이미지, 스크립트 파일, 스타일 시트 등 다양한 외부 리소스가 포함될 수 있습니다. 브라우저는 이러한 리소스를 필요에 따라 다운로드하고 표시합니다.

7. 브라우저 저장소와 캐싱:
브라우저는 쿠키, 로컬 스토리지, 세션 스토리지 등을 사용하여 사용자 정보와 데이터를 저장하며, 이전에 방문한 웹 사이트의 리소스를 캐싱하여 다음 방문 때 빠르게 로딩할 수 있습니다.

8. 보안 및 인증:
브라우저는 HTTPS를 통해 암호화된 연결로 데이터를 전송하고, 서버의 SSL/TLS 인증서를 확인하여 안전한 연결을 확보합니다.

이러한 단계를 통해 브라우저는 사용자가 웹 페이지와 상호작용하며 웹 콘텐츠를 효과적으로 표시할 수 있는 환경을 제공합니다.

사용자가 URL을 입력하면 브라우저는 서버에 해당 페이지 요청을 보내고, 서버는 HTML, CSS, JavaScript 등의 리소스로 응답합니다. 받은 HTML을 파싱하여 문서 객체 모델(DOM)로 변환하고, CSS 스타일을 적용하여 렌더링 트리를 구성합니다. 렌더링 트리를 기반으로 화면에 내용을 표시하고, JavaScript를 실행하여 페이지의 동적인 부분과 상호작용을 처리합니다.

 


< 자바 스프링에서 알림 기능을 구현하는 방법의 종류 >

1. 메시지 큐를 활용한 비동기 통신(실시간x)(Spring AMQP/Spring Kafka 활용)

더보기
  • 메시지 큐를 활용하여 애플리케이션 간 비동기적인 통신을 구현하는 것을 의미합니다.
  • Spring AMQP는 RabbitMQ와 같은 메시지 브로커를 사용하여 메시지 기반 통신을 지원합니다.
  • Spring Kafka는 Apache Kafka를 사용하여 메시지 기반 통신을 지원합니다.
  • 목적: 메시지 큐는 시스템 간 비동기적으로 메시지를 전송하는 데 사용됩니다. 대부분의 경우 실시간 통신이 아닌 이벤트를 처리하거나 작업을 예약하고자 할 때 활용됩니다.
  • 장점: 비동기 통신으로 작업을 처리하므로 작업의 순서와 우선순위를 조절하기 쉽습니다. 작업 부하가 많은 상황에서 성능 향상을 도모할 수 있습니다.
  • 접근 난이도: 메시지 큐 설정 및 메시지의 전송과 수신에 대한 이해가 필요합니다. 상대적으로 복잡하지만 유연성과 확장성이 높습니다.

 

2. 웹 소켓을 사용한 실시간 알림(Spring WebSocket 모듈 활용)

더보기
  • 웹 소켓을 활용하여 클라이언트와 서버 간 실시간 양방향 통신을 구현하는 것을 의미합니다.
  • Spring WebSocket 모듈을 사용하여 실시간 통신을 구현할 수 있습니다.
  • 웹 소켓을 사용하면 웹 애플리케이션 내에서 실시간으로 데이터를 주고받을 수 있습니다. 이를 통해 사용자들에게 빠르고 실시간으로 알림을 전달할 수 있습니다. 스프링 WebSocket 모듈을 활용하면 웹 소켓을 쉽게 구현하고 관리할 수 있으며, 초보자도 기본적인 개념을 이해하면서 실시간 알림을 구현할 수 있을 것입니다.
  • 목적: 웹 소켓은 클라이언트와 서버 간 양방향 실시간 통신을 제공합니다. 주로 실시간 알림, 채팅 등 실시간 상호작용이 필요한 상황에 사용됩니다.
  • 장점: 실시간 통신을 간단하게 구현할 수 있으며, 클라이언트와 서버 간의 상태 변화를 즉시 반영할 수 있습니다.
  • 접근 난이도: 웹 소켓을 사용하기 위해 WebSocket 프로토콜에 대한 이해와 클라이언트 및 서버에서 웹 소켓 통신을 설정해야 합니다. 비교적 직관적이고 빠른 구현이 가능합니다.

 

3. 스케줄링을 통한 주기적인 알림(특정 시간 또는 조건에 따라 주기적으로 알림을 전송해야 하는 경우)(@scheduled 어노테이션 활용)

더보기
  • 주기적으로 특정 작업을 실행하기 위해 스케줄링을 사용하는 것을 의미합니다.
  • Spring의 @Scheduled 어노테이션을 활용하여 주기적인 작업을 설정할 수 있습니다.

 

4. 이메일 또는 푸시 알림 서비스와 연동(이메일 발송 라이브러리, Firebase Cloud Messaging-푸시 알림 서비스 연동하여 모바일 애플리케이션 푸시 알림 보내기)

더보기
  • 이메일 발송을 위한 라이브러리나 서비스를 활용하여 이메일 알림을 구현하는 것을 의미합니다.
  • 이메일 알림은 기존에 많은 사람들이 사용하고 익숙한 방법 중 하나입니다. 스프링 프레임워크는 JavaMailSender를 통해 이메일 발송을 지원하며, 이를 활용하여 알림 이메일을 보내는 것은 비교적 간단합니다.
  • Firebase Cloud Messaging을 사용하여 모바일 애플리케이션에 푸시 알림을 보내는 것을 의미합니다.
  • 푸시 알림은 모바일 애플리케이션에서 사용되는 중요한 기능 중 하나입니다. Firebase Cloud Messaging(Firebase Cloud Messaging)과 같은 서비스를 활용하면 모바일 애플리케이션에 푸시 알림을 보내는 것이 비교적 간단하고 편리합니다.
  • 이메일 발송을 위한 JavaMailSender 라이브러리나 Firebase Cloud Messaging을 사용하여 푸시 알림을 구현하는 것은 초보자도 따라하기 쉽고, 자바 스프링의 기본 개념을 이해하면서 알림 기능을 구현할 수 있는 좋은 방법입니다.

 

5. 데이터베이스를 활용한 저장 및 조회(JPA 활용)

더보기
  • 데이터베이스를 사용하여 알림 관련 데이터를 저장하고 조회하는 것을 의미합니다.
  • Java Persistence API (JPA)를 활용하여 데이터베이스와의 상호작용을 단순화할 수 있습니다.

 

* 프로젝트의 필요 구현 사항에 맞춰 선택 필요!

 

참고 블로그
https://taemham.github.io/posts/Implementing_Notification/

 


< ' java.time ' 패키지 >

1. Instant : 특정 시점을 나타내는 클래스로, 에포크 시점(1970년 1월 1일 00:00:00 UTC)부터의 경과 시간을 초 단위로 표현

더보기
Instant now = Instant.now(); // 현재 시간을 가져옴
Instant later = now.plusSeconds(60); // 현재 시간에서 60초 뒤의 시간을 가져옴
boolean isBefore = now.isBefore(later); // now가 later보다 이전인지 확인

 

2. LocalDate : 날짜 정보만을 가지는 클래스로, 연, 월, 일을 표현

더보기
LocalDate today = LocalDate.now(); // 현재 날짜를 가져옴
LocalDate tomorrow = today.plusDays(1); // 오늘로부터 1일 뒤의 날짜를 가져옴
boolean isLeapYear = today.isLeapYear(); // 오늘이 윤년인지 확인

 

3. LocalTime : 시간 정보만을 가지는 클래스로, 시, 분, 초, 밀리초를 표현

더보기
LocalTime currentTime = LocalTime.now(); // 현재 시간을 가져옴
LocalTime laterTime = currentTime.plusHours(2); // 현재 시간에서 2시간 뒤의 시간을 가져옴
int hour = currentTime.getHour(); // 현재 시간의 시를 가져옴

 

4. LocalDateTime : 날짜와 시간 정보를 가지는 클래스로, LocalDate와 LocalTime을 합쳐서 표현

더보기
LocalDateTime currentDateTime = LocalDateTime.now(); // 현재 날짜와 시간을 가져옴
LocalDateTime futureDateTime = currentDateTime.plusDays(7).plusHours(3); // 현재로부터 7일 3시간 뒤의 날짜와 시간을 가져옴

 

5. ZonedDateTime : 특정 시간대 정보를 가지는 클래스로, 시간대까지 포함한 날짜와 시간을 표현

더보기
ZoneId newYorkZone = ZoneId.of("America/New_York");
ZonedDateTime newYorkTime = ZonedDateTime.now(newYorkZone); // 뉴욕 시간을 가져옴
ZoneId londonZone = ZoneId.of("Europe/London");
ZonedDateTime londonTime = newYorkTime.withZoneSameInstant(londonZone); // 뉴욕 시간을 런던 시간으로 변환

 

6. Duration : 시간 간격을 나타내는 클래스로, 초와 나노초 단위로 표현

더보기
Instant start = Instant.now();
// 무거운 작업 실행
Instant end = Instant.now();
Duration timeElapsed = Duration.between(start, end); // 작업에 걸린 시간 계산
long seconds = timeElapsed.getSeconds(); // 초 단위로 시간 계산

 

7. Period : 날짜 간격을 나타내는 클래스로, 연, 월, 일 단위로 표현

더보기
LocalDate today = LocalDate.now();
LocalDate futureDate = today.plusYears(2).plusMonths(3).plusDays(5); // 오늘로부터 2년 3개월 5일 뒤의 날짜를 가져옴
Period period = Period.between(today, futureDate); // 날짜 간격 계산
int years = period.getYears(); // 연도 간격을 가져옴

 


< 이메일 알림 기능 구현 중 트러블슈팅 >

1. JavaMailSender 사용 중 보내는 메일 계정의 비밀번호 문제

더보기
  • 발생한 예외
// Run Console

org.springframework.mail.MailAuthenticationException: Authentication failed
(생략)
Caused by: jakarta.mail.AuthenticationFailedException: 534-5.7.9 Application-specific password required. Learn more at
534 5.7.9  https://support.google.com/mail/?p=InvalidSecondFactor e6-20020a170902744600b001b9c5e07bc3sm10793326plt.238 - gsmtp

 

  • 예외가 발생한 코드
// application.properties

spring.mail.password=${GMAIL_PASSWORD}

 

  • 원인 분석
구글 계정으로 이메일을 발신할 때 구글 계정 2단계 인증을 통한 앱 비밀번호를 취득해야 함

 

  • 해결 방법
구글 계정 -> 보안 -> 2단계 인증 -> 앱 비밀번호 -> 생성 -> 해당 비밀번호로 GMAIL_PASSWORD 환경변수 대체

 

2. LocalDate와 LocalDateTime 차이로 인한 예외 발생

더보기
  • 발생한 예외
// Run console

java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: 2023-08-23 of type java.time.LocalDate

 

  • 예외가 발생한 코드
// reminderResponseDto

private long getDaysDifference(Festival festival) {
        LocalDate today = LocalDate.now();
        LocalDateTime festivalDate = festival.getOpenDate();
        return ChronoUnit.DAYS.between(festivalDate, today);
    }

 

  • 원인 분석
LocalDate와 LocalDateTime 호환성 + 순서가 잘못된 문제로 인해 예외 발생

 

  • 수정한 코드
    private long getDaysDifference(Festival festival) {
        LocalDate today = LocalDate.now();
        LocalDateTime festivalDateTime = festival.getOpenDate();
        LocalDate festivalDate = festivalDateTime.toLocalDate();
        return ChronoUnit.DAYS.between(today, festivalDate);
    }

 

3. User를 불러오는 과정에서 연결된 객체를 DB에서 가져오지 못하는 문제 발생

더보기
  • 발생한 예외
// Run console

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.sparta.lafesta.festival.entity.Festival.festivalLikes: could not initialize proxy - no Session

org.hibernate.LazyInitializationException: could not initialize proxy [com.sparta.lafesta.user.entity.User#1] - no Session

 

  • 예외가 발생한 코드
class FestivalReminderResponseDto

    this.festivalLikeUsersEmail = festival.getFestivalLikes().stream()
                .map(FestivalLike::getUser)
                .map(User::getEmail)
                .collect(Collectors.toList());

 

  • 원인 분석
연결된 Entity의 FetchType이 LAZY로 설정되어 발생한 문제

 

  • 수정한 코드
    위의 코드는 그대로 두고
    
    class Festival (Entity)
    
    @OneToMany(mappedBy = "festival", orphanRemoval = true, fetch = FetchType.EAGER)
    private List<FestivalLike> festivalLikes = new ArrayList<>();
    
    
    class FestivalLike (Entity)
    
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "userId")
    private User user;

 

  • 추가 발생 문제
FetchType.EAGER -> 성능에 문제가 되는 것으로 알고 있어 해당 부분 개선 필요

 

  • 해결 방법
왜인지는 모르겠으나 FestivalReminderResponseDto를 받는 메소드를 NotificationService에서 FestivalServiceImpl로 옮기고 @transactional(readonly = true)를 추가하니 문제가 해결됨 -> @transactional
(readonly = true) 추가 후 FestivalServiceImpl과 똑같은 환경을 NotificationService에 주어봐도 해당 위치에서는 오류가 발생해 왜 해결된건지 영문을 모르겠음.. ->왜 해결된건지 아시는 분 알려주세요 너무 궁금해요...

 

  • 수정한 코드
    위의 코드는 LAZY로 복귀
    
    class Festival (Entity)
    
    @OneToMany(mappedBy = "festival", orphanRemoval = true)
    private List<FestivalLike> festivalLikes = new ArrayList<>();
    
    
    class FestivalLike (Entity)
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "userId")
    private User user;
    
    
   아래 메소드가 class NotificationService에서 아래 class로 이동

    class FestivalServiceImpl implements FestivalService
    
    // 알림을 보낼 페스티벌 가져오기
    @Override
    @Transactional(readOnly = true)
    public List<FestivalReminderResponseDto> getFestivalReminders() {
        // 페스티벌 개최 당일, 1일 전, 7일 전 발송
        LocalDate today = LocalDate.now();
        LocalDate tomorrow = today.plusDays(1);
        LocalDate sevenDaysAfter = today.plusDays(7);

        List<LocalDate> dateRanges = Arrays.asList(today, tomorrow, sevenDaysAfter);

        List<Festival> reminderFestivals = dateRanges.stream()
                .map(date -> {
                    LocalDateTime startOfDay = date.atStartOfDay();
                    LocalDateTime endOfDay = date.atTime(LocalTime.MAX);
                    return festivalRepository.findAllByOpenDateBetween(startOfDay, endOfDay);
                })
                .flatMap(List::stream)
                .collect(Collectors.toList());

        return reminderFestivals.stream().map(FestivalReminderResponseDto::new).toList();
    }

 

  • FetchType.EAGER의 성능 저하 이슈
더보기
  • FetchType.EAGER는 JPA(Java Persistence API)에서 엔티티 간의 관계를 로딩하는 방식 중 하나입니다. 이 방식은 연관된 엔티티를 즉시 로딩하여 엔티티를 조회할 때 관련된 모든 데이터를 한 번에 가져오는 것을 의미합니다. 반면에 FetchType.LAZY는 연관된 엔티티를 실제로 사용할 때까지 로딩을 미루는 지연 로딩 방식입니다.

 

  • FetchType.EAGER는 사용자가 엔티티를 조회할 때 관련된 모든 데이터를 즉시 가져오므로 데이터베이스에서 모든 필요한 정보를 로드하게 됩니다. 이로 인해 몇 가지 성능 이슈가 발생할 수 있습니다.
    • 불필요한 데이터 로딩: FetchType.EAGER를 사용하면 연관된 엔티티의 모든 데이터가 로딩되기 때문에 실제로 사용하지 않을 데이터까지 로딩되어 불필요한 데이터 전송이 발생할 수 있습니다. 이는 데이터베이스 및 네트워크 자원을 낭비할 수 있습니다.
    • N+1 쿼리 문제: FetchType.EAGER를 사용하면 연관된 엔티티 각각에 대해 추가 쿼리가 실행되어 데이터를 가져오는데, 이는 N+1 쿼리 문제를 야기할 수 있습니다. 이는 연관된 엔티티의 개수에 따라 성능 문제를 야기할 수 있습니다.
    • 메모리 부족: FetchType.EAGER를 사용하면 모든 연관 엔티티를 메모리에 즉시 로딩하므로 대량의 데이터를 다룰 때 메모리 부족 문제가 발생할 수 있습니다.
    • 조회 성능 저하: 데이터베이스에서 모든 필요한 데이터를 한 번에 로딩하므로 데이터베이스 조회 성능이 저하될 수 있습니다.

 

  • 따라서 FetchType.EAGER는 필요한 경우가 아니라면 지양하는 것이 좋습니다. 대부분의 상황에서는 FetchType.LAZY를 사용하여 연관 엔티티를 지연 로딩하는 것이 성능 및 자원 사용 측면에서 더 효율적입니다. FetchType.LAZY를 사용하면 실제로 데이터가 필요한 시점에만 연관 엔티티를 로딩하여 데이터를 효율적으로 관리할 수 있습니다.

< CI 환경 구축 트러블슈팅 >

 

1. MySQl 설치 부분

더보기
  • 발생한 예외
// Github Actions - build

[build](https://github.com/LaFesta7/LikeFesta/actions/runs/5951078786/job/16140180506#step:6:1)
Unexpected input(s) 'host port', 'container port', 'character set server', 'collation server', valid inputs are ['entryPoint', 'args', 'mysql version', 'mysql database', 'mysql user', 'mysql password']

 

  • 예외가 발생한 코드
// dev-ci.yml

- name: MySQL 설치
        uses: samin/mysql-action@v1
        with:
          host port: 3306 # Optional, default value is 3306. The port of host
          container port: 3307 # Optional, default value is 3306. The port of container
          character set server: 'utf8' # Optional, default value is 'utf8mb4'. The '--character-set-server' option for mysqld
          collation server: 'utf8_general_ci' # Optional, default value is 'utf8mb4_general_ci'. The '--collation-server' option for mysqld
          mysql version: '8.0' # Optional, default value is "latest". The version of the MySQL
          mysql database: test # Optional, default value is "test". The specified database which will be create
          mysql user: developer # Required if "mysql root password" is empty, default is empty. The superuser for the specified database. Of course you can use secrets, too
          mysql password: ${{ secrets.DB_PASSWORD }}

 

  • 원인 분석
GitHub Actions의 빌드 설정 파일에서 MySQL 관련 설정 부분에서 발생하는 문제. 현재 yml 파일에 MySQL 관련 설정이 포함되어 있지만, GitHub Actions에서 사용하는 actions/checkout@v3 액션은 기본적으로 컨테이너 환경 내에서 코드를 실행하므로, MySQL과 같은 데이터베이스 서버를 직접 설치하고 구성하는 것은 불필요 -> 외부 MYSQL 서비스 사용하는 방법으로 수정

 

  • 수정한 코드
// dev-ci.yml

(위의 오류 코드 삭제 후 steps 위에 추가)

services:
      mysql:
        image: mysql:latest
        env:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: test_db
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping"

 

2. Gradle with Build

더보기
  • 발생한 예외
// Github Actions - build

java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@290807e5 testClass = com.sparta.lafesta.LaFestaApplicationTests, locations = [], classes = [com.sparta.lafesta.LaFestaApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@5143c662, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@5b1ebf56, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@4f25b795, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@4fad9bb2, org.springframework.boot.test.context.SpringBootTestAnnotation@c361b062], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]

 

  • 예외가 발생한 코드
// dev-ci.yml

 

  • 원인 분석
다른 CI 환경 구축 yml 파일들을 비교하다 보니 Gradle을 Build 하는 부분이 없어 문제가 생긴 것을 알게 됨

 

  • 수정한 코드
// dev-ci.yml

(해당 코드 추가)

- name: Build with Gradle
        uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
        with:
          arguments: build

 

3. 환경변수 설정 관련 오류

더보기
  • 발생한 예외
// Github Actions - build

java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@290807e5 testClass = com.sparta.lafesta.LaFestaApplicationTests, locations = [], classes = [com.sparta.lafesta.LaFestaApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@5143c662, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@5b1ebf56, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@4f25b795, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@4fad9bb2, org.springframework.boot.test.context.SpringBootTestAnnotation@c361b062], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]

 

  • 예외가 발생한 코드
// dev-ci.yml

- name: yml 파일 생성
        run: |
          cd ./src/main/resources
          rm -rf ./application.properties
          touch ./application.yml
          echo "${{ secrets.APPLICATION_YML }}" > ./application.yml
          touch ./application-aws.yml
          echo "${{ secrets.APPLICATION_AWS_YML }}" > ./application-aws.yml
          touch ./application-key.yml
          echo "${{ secrets.APPLICATION_KEY_YML }}" > ./application-key.yml
        shell: bash

 

  • 원인 분석
Github Actions secrets and variables 에서 환경변수를 설정하는 파일을 만들어 환경변수를 설정해야하지만 해당 부분을 잘 만들지 못해 오류가 발생

 

  • 수정한 코드
dev-ci.yml

(위의 코드 삭제 후 services 위에)

env:
      DB_URL: jdbc:mysql://localhost:3306/test_db
      DB_USER: root
      DB_PASSWORD: root
      JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
      KAKAO_REST_API: ${{ secrets.KAKAO_REST_API }}

 


Next...

  • 최종 프로젝트 회의 및 작성
  • 기술면접 대비 공부
  • JPA 강의 듣기
  • 알고리즘 문제 풀기
  • 뚜까패 스터디 발표 자료 정리
  • 원티드 프리온보딩 백엔드챌린지 9월 과제 수행
Comments