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