Backend
home

2025-6-23 (월)

생성일
2025/06/22 23:23
태그
Jenkins
JPA

Jenkins 심화 - profile 적용(분기 처리)

폴더 구조 변경

backendProject 하위폴더에도 Dockerfile과 nginx.conf를 추가해준다.
FROM nginx:latest # 기존 설정 제거 (선택적) RUN rm /etc/nginx/nginx.conf # 커스텀 nginx.conf 복사 COPY nginx.conf /etc/nginx/nginx.conf
Docker
복사
worker_processes 1; events { worker_connections 1024; } http { upstream spring_backend { server backend1:8080; server backend2:8080; server backend3:8080; } server { listen 80; location / { proxy_pass http://spring_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /ws-chat { proxy_pass http://spring_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /ws-gpt { proxy_pass http://spring_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } }
Plain Text
복사
backendProject > docker-compose.backend.yml 생성
services: # redis: # image: redis # container_name: redis # ports: # - "6379:6379" # # # database: # image: mysql:8 # container_name: database # ports: # - "3307:3306" # environment: # MYSQL_DATABASE: backendDB # MYSQL_ROOT_PASSWORD: ${DB_PASS} # volumes: # - ./volumes/mysql-data:/var/lib/mysql #내가 지정한 경로 # healthcheck: # test: ["CMD", "mysqladmin", "ping", "-h", "localhost", '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD'] # timeout: 20s # retries: 10 backend1: # 서비스 이름은 컨테이너간 통신하기 위한 이름 build: . container_name: backend1 environment: PROJECT_NAME: 백앤드 서버1 DB_SERVER: ${DB_SERVER} DB_PORT: ${DB_PORT} DB_USER: ${DB_USER} DB_PASS: ${DB_PASS} REDIS_HOST: ${REDIS_HOST} networks: - prod_server # depends_on: # - database # - redis backend2: # 서비스 이름은 컨테이너간 통신하기 위한 이름 build: . container_name: backend2 environment: PROJECT_NAME: 백앤드 서버2 DB_SERVER: ${DB_SERVER} DB_PORT: ${DB_PORT} DB_USER: ${DB_USER} DB_PASS: ${DB_PASS} REDIS_HOST: ${REDIS_HOST} networks: - prod_server # depends_on: # - database # - redis backend3: # 서비스 이름은 컨테이너간 통신하기 위한 이름 build: . container_name: backend3 environment: PROJECT_NAME: 백앤드 서버3 DB_SERVER: ${DB_SERVER} DB_PORT: ${DB_PORT} DB_USER: ${DB_USER} DB_PASS: ${DB_PASS} REDIS_HOST: ${REDIS_HOST} # depends_on: # - database # - redis networks: - prod_server nginx: build: ./nginx/. container_name: nginx ports: - "80:80" # volumes: # - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - backend1 - backend2 - backend3 networks: - prod_server networks: prod_server: external: true
YAML
복사
파일 구조 변경으로 인한 각 파일들 내용 수정
Data 폴더 안에 docker-compose.data.yml 작성
.env 파일 추가
services: # docker network create prod_server 도커 네트워크 만들어야 함! (명령어 실행) redis: image: redis container_name: redis ports: - "6379:6379" networks: - prod_server restart: unless-stopped database: image: mysql:8 container_name: database ports: - "3307:3306" environment: MYSQL_DATABASE: backendDB MYSQL_ROOT_PASSWORD: ${DB_PASS} volumes: - ./volumes/mysql-data:/var/lib/mysql #내가 지정한 경로 healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD'] timeout: 20s retries: 10 networks: - prod_server restart: unless-stopped networks: prod_server: external: true
YAML
복사
Jenkins Script 수정 (localhost:7070 파이프라인 쪽에 적용)
pipeline { agent any environment { PROJECT_DIR = '/var/jenkins_home/workspace/backend5_Test_local/backendProject' // 백앤드 서버 프로젝트 폴더 COMPOSE_FILE = 'docker-compose.backend.yml' } stages { stage('Cleanup Containers') { steps { dir("${PROJECT_DIR}") { sh "docker-compose -f ${COMPOSE_FILE} down" } } } stage('Build') { steps { dir("${PROJECT_DIR}") { sh 'chmod +x gradlew' sh './gradlew clean build' } } } stage('Compose Up') { steps { dir("${PROJECT_DIR}") { sh "docker-compose -f ${COMPOSE_FILE} up -d --build" } } } stage('Restart Nginx') { steps { sh 'docker restart nginx || true' } } } }
Shell
복사
test 관련 파일 설정 모두 해제
plugins { id 'java' id 'org.springframework.boot' version '3.5.0' id 'io.spring.dependency-management' version '1.1.7' } group = 'org.example' version = '0.0.1-SNAPSHOT' java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' // 20250620 - H2 database 추가 // testImplementation 'com.h2database:h2' (주석 처리) ... 중략 ... } tasks.named('test') { useJUnitPlatform() }
Plain Text
복사
#spring.datasource.url=jdbc:h2:mem:testdb #spring.datasource.driver-class-name=org.h2.Driver #spring.datasource.username=sa #spring.datasource.password= #spring.jpa.hibernate.ddl-auto=create-drop #spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
Shell
복사
package org.example.backendproject; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; //@ActiveProfiles("test") @SpringBootTest class BackendProjectApplicationTests { @Test void contextLoads() { } }
Java
복사
package org.example.backendproject.stompwebsocket.redis; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; //@Profile("!test") @RequiredArgsConstructor @Configuration public class RedisConfig { private final RedisSubscriber redisSubscriber; @Bean public RedisMessageListenerContainer container(RedisConnectionFactory redisConnectionFactory) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(redisConnectionFactory); container.addMessageListener(new MessageListenerAdapter(redisSubscriber), new PatternTopic("room.*")); container.addMessageListener(new MessageListenerAdapter(redisSubscriber), new PatternTopic("private.*")); // 귓속말 return container; } }
Java
복사
profile 설정: application.properties - dev, prod 분기
application-properties
spring.application.name=backendProject # Jenkins spring.profiles.active=prod # local #spring.profiles.active=dev
YAML
복사
application-dev.properties
spring.application.name=backendProject # 로컬 개발용 db.server=${DB_SERVER:localhost} db.port=${DB_PORT:3307} db.username=${DB_USER:root} db.password=${DB_PASS:1234} REDIS.HOST=${REDIS_HOST:localhost} spring.data.redis.host=${REDIS.HOST} spring.data.redis.port=6379 spring.datasource.url=jdbc:mysql://${db.server}:${db.port}/backendDB?serverTimezone=Asia/Seoul&characterEncoding=UTF-8&rewriteBatchedStatements=true spring.datasource.username=${db.username} spring.datasource.password=${db.password} openai.api.key=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 spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect spring.jpa.hibernate.ddl-auto=update
YAML
복사
application-prod.properties
spring.application.name=backendProject # 환경 변수에 값이 있으면 환경변수의 값을 쓰고 # 환경 변수에 값이 없으면 뒤에 있는 default 값을 씀 # 젠킨스 배포용 db.server=${DB_SERVER:database} db.port=${DB_PORT:3306} db.username=${DB_USER:root} db.password=${DB_PASS:1234} REDIS.HOST=${REDIS_HOST:redis} spring.data.redis.host=${REDIS.HOST} spring.data.redis.port=6379 spring.datasource.url=jdbc:mysql://${db.server}:${db.port}/backendDB?serverTimezone=Asia/Seoul&characterEncoding=UTF-8&rewriteBatchedStatements=true spring.datasource.username=${db.username} spring.datasource.password=${db.password} openai.api.key=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 spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect spring.jpa.hibernate.ddl-auto=update
YAML
복사
Jenkins 실행
참고로 Jenkins 실행하기 전에 redis와 mysql 컨테이너를 수동으로 실행 시켜줘야 함
docker-compose -f docker-compose.data.yml up -d
Shell
복사

에러 확인

젠킨스 컨테이너 삭제 후 재실행하니 정상적으로 동작됨
BackendProjectApplicationTests > contextLoads() FAILED java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180 Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:1826 Caused by: jakarta.persistence.PersistenceException at AbstractEntityManagerFactoryBean.java:431 Caused by: org.hibernate.exception.JDBCConnectionException at SQLStateConversionDelegate.java:100 Caused by: com.mysql.cj.jdbc.exceptions.CommunicationsException at SQLError.java:165 Caused by: com.mysql.cj.exceptions.CJCommunicationsException at null:-1 Caused by: java.net.UnknownHostException at null:-1
Shell
복사

JPA

Spring Data JPA

1. Spring에서 사용하는 데이터 접근 방식
JPA(Java Persistence API)
JPA는 ORM 기술을 사용한 인터페이스의 집합
JPA는 인터페이스만 있고 직접 동작하는 구현체는 없음
JPA의 대표적인 구현체(실제로 동작하는 코드)
Hibernate
구현체는 JPA의 인터페이스의 명령을 보고 SQL을 만들며 DB와 통신하는 역할을 수행
ORM (Object Relational Mapping)
애플리케이션의 객체와 관계형 데이터베이스의 테이블을 자동으로 연결해주는 기술
ORM의 주요 기능
객체와 테이블 매핑
객체의 속성을 데이터베이스 테이블의 컬럼과 자동으로 매핑함
SQL 자동 생성
ORM은 객체 지향 코드만 작성하면 자동으로 SQL 쿼리를 생성하여 실행
데이터베이스 연산
객체 지향 방식으로 데이터베이스의 CRUD 연산을 수행할 수 있음
개발 생산성 향상
ORM을 사용하면 데이터베이스 관련 코드를 줄이고 개발 생산성을 높일 수 있음
유지보수 용이
ORM을 사용하면 데이터베이스 구조 변경이 쉽게 반영되고, 유지보수 작업이 용이해짐
EntityManager ← DB와 상호작용
JPA의 인터페이스 중 가장 핵심 인터페이스
2. Spring Data JPA
Spring에서 JPA(Java Persistence API)를 더 쉽게, 더 생산적으로 사용할 수 있도록 Spring에서 만든 확장 라이브러리
3. Spring Data JPA의 특징
기본 CRUD 메서드 제공
SQL 자동 생성
Spring Data JPA에서 발생하는 일
3계층 구조
Controller → Service → Repository
Controller: 사용자의 요청 처리 및 응답 처리
Service: 비즈니스 로직 처리 (저장, 수정, 삭제, 조회)
Repository: Database 통신 담당

실습 진행 방식

회원 가입, 내 정보 수정, 게시판 글쓰기, 댓글 달기, 댓글에 댓글 달기
로그인에 Security 적용
소셜 로그인 적용 (OAuth2)
로그 수집 대시보드
서버 성능 모니터링 대시보드
⇒ 6/23(월) 기준 회원 가입과 로그인까지 진행

JPA 심화

Spring Data JPA의 엔티티 연관 관계 매핑
연관관계의 방향 (단방향, 양방향)
연관관계의 종류 (1:1, N:1, 1:N, N:N)
연관관계의 주인
Fetch 전략 (즉시 로딩, 지연 로딩)
쿼리 메서드 자동 생성
Auditing(자동 등록자/수정자, 등록일, 수정일 관리)
직접 JPQL, 네이티브 쿼리 작성 (게시글 검색)
페이징, 정렬 지원 (게시글 검색 페이징)
대용량 데이터 처리 (배치 insert)
1:1 (OneToOne) 관계
회원가입 (회원 프로필)
한명의 회원은 하나의 프로필(상세정보, 주소)을 가짐
1:N(OneToMany) 관계 - 단방향
Order 클래스에는 member 필드가 없음, Order에서 member를 알 수 없음
Order에서 member를 알 수 없음
단방향에서 생기는 문제
⇒ Order에서 member를 알 수가 없어서 객체 접근 불가능 (order.getMember() ← 불가능)
1:N(OneToMany, ManyToOne) 관계 - (양방향)
CascadeType 종류
Fetch 전략
쿼리 메서드 SQL 자동 생성
메서드 이름만 잘 지으면 JPA가 JPQL 쿼리를 자동으로 생성

‘회원가입 + 로그인’ 실습

application.properties
spring.application.name=backendProject # Jenkins #spring.profiles.active=prod # local spring.profiles.active=local + # dev #spring.profiles.active=dev
Shell
복사
application-local.properties
spring.application.name=backendProject db.server=${DB_SERVER:localhost} db.port=${DB_PORT:3307} db.username=${DB_USER:root} db.password=${DB_PASS:1234} REDIS.HOST=${REDIS_HOST:localhost} spring.data.redis.host=${REDIS.HOST} spring.data.redis.port=6379 spring.datasource.url=jdbc:mysql://${db.server}:${db.port}/backendDB?serverTimezone=Asia/Seoul&characterEncoding=UTF-8&rewriteBatchedStatements=true spring.datasource.username=${db.username} spring.datasource.password=${db.password} openai.api.key=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 spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect spring.jpa.hibernate.ddl-auto=update
YAML
복사
User
package org.example.backendproject.user.entity; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.OneToOne; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.example.backendproject.Auth.entity.Auth; @NoArgsConstructor @AllArgsConstructor @Getter @Setter @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String userid; @Column(nullable = false) private String password; @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private UserProfile userProfile; @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Auth auth; }
Java
복사
UserProfile
package org.example.backendproject.user.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToOne; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @AllArgsConstructor @NoArgsConstructor @Getter @Setter @Entity public class UserProfile { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String username; @Column(nullable = false, unique = true) private String email; @Column(nullable = false) private String phone; @Column(nullable = false) private String address; @OneToOne // 1:1 관계 @JoinColumn(name = "user_id") private User user; }
Java
복사
UserRepository
package org.example.backendproject.user.repository; import java.util.Optional; import org.example.backendproject.user.entity.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByUserid(String userid); }
Java
복사
UserDTO
package org.example.backendproject.user.dto; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class UserDTO { private Long id; private String userid; private String username; private String email; private String phone; private String address; }
Java
복사
Auth
package org.example.backendproject.Auth.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToOne; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.example.backendproject.user.entity.User; @NoArgsConstructor @AllArgsConstructor @Getter @Setter @Entity public class Auth { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String tokenType; @Column(nullable = false) private String accessToken; @Column(nullable = false) private String refreshToken; @OneToOne(fetch = FetchType.LAZY) // 1:1 관계, 지연로딩 적용 -> Auth 엔티티 조회할 때 user 객체는 불러오지 않음 @JoinColumn(name = "user_id") // auth.getUser()에 실제로 접근할 때 User 쿼리 발생!! private User user; }
Java
복사
SignUpRequestDto
package org.example.backendproject.Auth.dto; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class SignUpRequestDTO { private String userid; private String password; private String username; private String email; private String phone; private String address; }
Java
복사
LoginRequestDTO
package org.example.backendproject.Auth.dto; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class LoginRequestDTO { private String userid; private String password; }
Java
복사
AuthRepository
package org.example.backendproject.Auth.repository; import org.example.backendproject.Auth.entity.Auth; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface AuthRepository extends JpaRepository<Auth, Long> { }
Java
복사
AuthService
package org.example.backendproject.Auth.service; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.example.backendproject.Auth.dto.LoginRequestDTO; import org.example.backendproject.Auth.dto.SignUpRequestDTO; import org.example.backendproject.user.dto.UserDTO; import org.example.backendproject.user.entity.User; import org.example.backendproject.user.entity.UserProfile; import org.example.backendproject.user.repository.UserRepository; import org.springframework.stereotype.Service; @RequiredArgsConstructor @Service public class AuthService { private final UserRepository userRepository; @Transactional // 해당 어노테이션 선언해야 저장이 된다. public void signUp(SignUpRequestDTO dto) { // 사용자 조회 여부 확인, null값 체크 if (userRepository.findByUserid(dto.getUserid()).isPresent()) { throw new RuntimeException("사용자가 이미 존재합니다."); } User user = new User(); user.setUserid(dto.getUserid()); user.setPassword(dto.getPassword()); UserProfile profile = new UserProfile(); profile.setUsername(dto.getUsername()); profile.setEmail(dto.getEmail()); profile.setPhone(dto.getPhone()); profile.setAddress(dto.getAddress()); /** 연관관계 설정 **/ profile.setUser(user); user.setUserProfile(profile); userRepository.save(user); } public UserDTO login(LoginRequestDTO loginRequestDTO) { User user = userRepository.findByUserid(loginRequestDTO.getUserid()) .orElseThrow(() -> new RuntimeException("사용자를 찾을 수 없습니다.")); if (!loginRequestDTO.getPassword().equals(user.getPassword())) { throw new RuntimeException("비밀번호를 찾을 수 없습니다."); } UserDTO userDTO = new UserDTO(); userDTO.setId(user.getId()); userDTO.setUserid(user.getUserid()); userDTO.setUsername(user.getUserProfile().getUsername()); userDTO.setEmail(user.getUserProfile().getEmail()); userDTO.setPhone(user.getUserProfile().getPhone()); userDTO.setAddress(user.getUserProfile().getAddress()); return userDTO; } }
Java
복사
AuthController
package org.example.backendproject.Auth.controller; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.example.backendproject.Auth.dto.LoginRequestDTO; import org.example.backendproject.Auth.dto.SignUpRequestDTO; import org.example.backendproject.Auth.service.AuthService; import org.example.backendproject.user.dto.UserDTO; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor @RestController @RequestMapping("/api/auth") @Slf4j public class AuthController { private final AuthService authService; /** 회원가입 **/ @PostMapping("/signUp") public ResponseEntity<String> signUp(@RequestBody SignUpRequestDTO signUpRequestDTO) { try { authService.signUp(signUpRequestDTO); return ResponseEntity.ok("회원가입 성공"); } catch (Exception e) { log.error(e.getMessage()); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); // 401 } } /** 로그인 **/ @PostMapping("/login") public ResponseEntity<UserDTO> login(@RequestBody LoginRequestDTO loginRequestDTO) { try { UserDTO loginUser = authService.login(loginRequestDTO); log.info("로그인 성공 = " + new ObjectMapper().writeValueAsString(loginRequestDTO)); return ResponseEntity.ok(loginUser); } catch (Exception e) { log.error(e.getMessage()); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); // 401 } } }
Java
복사
Postman 테스트
로그인 후 화면
DB에서 회원가입된 user 확인
로그를 통한 로그인 성공 확인

오늘 푸시한 커밋 리스트

날짜
커밋 메시지
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23
2025-06-23