diff --git a/src/main/java/com/aolda/itda/controller/certificate/CertificateController.java b/src/main/java/com/aolda/itda/controller/certificate/CertificateController.java
index cf5e160f14b9e873f554e32d18c6fa9b0f7d4096..d08968bd39f0076c4ffcaf6d3881b792020f8c7c 100644
--- a/src/main/java/com/aolda/itda/controller/certificate/CertificateController.java
+++ b/src/main/java/com/aolda/itda/controller/certificate/CertificateController.java
@@ -1,5 +1,6 @@
 package com.aolda.itda.controller.certificate;
 
+import com.aolda.itda.dto.PageResp;
 import com.aolda.itda.dto.certificate.CertificateDTO;
 import com.aolda.itda.service.certificate.CertificateService;
 import jakarta.servlet.http.HttpServletRequest;
@@ -19,17 +20,17 @@ public class CertificateController {
     /**
      * 인증서 생성
      * POST /api/certificate?projectId=xxx
-     * Body: CertificateDTO
      */
     @PostMapping("/certificate")
-    public ResponseEntity<Object> create(@RequestParam String projectId,
-                                         @RequestBody CertificateDTO dto,
-                                         HttpServletRequest request) {
-        System.out.println("1");
+    public ResponseEntity<Void> create(
+            @RequestParam String projectId,
+            @RequestBody CertificateDTO dto,
+            HttpServletRequest request
+    ) {
         certificateService.createCertificate(
                 projectId,
                 dto,
-                (List<String>) request.getAttribute("projects") // Forwarding과 동일하게
+                (List<String>) request.getAttribute("projects")
         );
         return ResponseEntity.ok().build();
     }
@@ -39,8 +40,10 @@ public class CertificateController {
      * GET /api/certificate?certificateId=xxx
      */
     @GetMapping("/certificate")
-    public ResponseEntity<Object> view(@RequestParam Long certificateId,
-                                       HttpServletRequest request) {
+    public ResponseEntity<CertificateDTO> view(
+            @RequestParam Long certificateId,
+            HttpServletRequest request
+    ) {
         return ResponseEntity.ok(
                 certificateService.getCertificate(
                         certificateId,
@@ -50,25 +53,29 @@ public class CertificateController {
     }
 
     /**
-     * 인증서 목록 조회
-     * GET /api/certificates?projectId=xxx
+     * 인증서 목록 조회 (domain 필터링 optional)
+     * GET /api/certificates?projectId=xxx&domain=foo
      */
     @GetMapping("/certificates")
-    public ResponseEntity<Object> lists(@RequestParam String projectId) {
+    public ResponseEntity<PageResp<CertificateDTO>> lists(
+            @RequestParam String projectId,
+            @RequestParam(required = false) String domain
+    ) {
         return ResponseEntity.ok(
-                certificateService.getCertificates(projectId)
+                certificateService.getCertificates(projectId, domain)
         );
     }
 
     /**
      * 인증서 수정
      * PATCH /api/certificate?certificateId=xxx
-     * Body: CertificateDTO
      */
     @PatchMapping("/certificate")
-    public ResponseEntity<Object> edit(@RequestParam Long certificateId,
-                                       @RequestBody CertificateDTO dto,
-                                       HttpServletRequest request) {
+    public ResponseEntity<Void> edit(
+            @RequestParam Long certificateId,
+            @RequestBody CertificateDTO dto,
+            HttpServletRequest request
+    ) {
         certificateService.editCertificate(
                 certificateId,
                 dto,
@@ -82,14 +89,14 @@ public class CertificateController {
      * DELETE /api/certificate?certificateId=xxx
      */
     @DeleteMapping("/certificate")
-    public ResponseEntity<Object> delete(@RequestParam Long certificateId,
-                                         HttpServletRequest request) {
+    public ResponseEntity<Void> delete(
+            @RequestParam Long certificateId,
+            HttpServletRequest request
+    ) {
         certificateService.deleteCertificate(
                 certificateId,
                 (List<String>) request.getAttribute("projects")
         );
         return ResponseEntity.ok().build();
     }
-    /*인증서 검색*/
-
 }
diff --git a/src/main/java/com/aolda/itda/dto/certificate/CertificateDTO.java b/src/main/java/com/aolda/itda/dto/certificate/CertificateDTO.java
index e37b75e531e2cdcd0487a9db4df45042368e0010..dcdefcf7841391251062aa0397babca938d40b4d 100644
--- a/src/main/java/com/aolda/itda/dto/certificate/CertificateDTO.java
+++ b/src/main/java/com/aolda/itda/dto/certificate/CertificateDTO.java
@@ -14,14 +14,17 @@ import java.time.LocalDateTime;
 public class CertificateDTO {
 
     private Long certificateId;          // 인증서 고유 ID
-    private String projectId;            // 프로젝트 식별자
+//    private String projectId;            // 프로젝트 식별자
     private String domain;               // SSL 인증받을 도메인 주소
     private String email;                // 도메인 소유자의 이메일
     private LocalDateTime expiredAt;     // 인증서 만료일
+    private LocalDateTime createdAt;    // 인증서 생성일
+    private LocalDateTime updatedAt;    // 인증서 업데이트일
     private Challenge challenge;         // 챌린지 방식 (HTTP, DNS_CLOUDFLARE)
     private Boolean isDeleted;           // 삭제 여부 (soft delete)
-    private String description;          // 설명 (필요시 자유롭게 작성)
+    private String apiToken;
 }
 /* 이메일, 챌린지 방식, http인지 dns인지... "*/
 //도메인, 소유자 이메일, 챌린지 방식 확실하게 들어가야함!!
 /*erd 보고 만들기*/
+//Challenge 키는 따로 (private으로 api 키 받기)
\ No newline at end of file
diff --git a/src/main/java/com/aolda/itda/entity/certificate/Certificate.java b/src/main/java/com/aolda/itda/entity/certificate/Certificate.java
index 489138ff8f7af05c064e395f3178e71afbc4352b..6f52656623f073abf365f356a9e795f8f9a7ff6c 100644
--- a/src/main/java/com/aolda/itda/entity/certificate/Certificate.java
+++ b/src/main/java/com/aolda/itda/entity/certificate/Certificate.java
@@ -32,15 +32,15 @@ public class Certificate extends BaseTimeEntity {
     @Column(length = 64)
     private String email;
 
-    private LocalDateTime expiredAt;
+    private LocalDateTime expiredAt; //인증서 만료일
+    private LocalDateTime createdAt;
+    private LocalDateTime updatedAt;
 
     @Enumerated(EnumType.STRING)
     private Challenge challenge;
 
     private Boolean isDeleted;
 
-    @Column(length = 256)
-    private String description;
 
     public String formatDomain() {
         return domain == null ? null : domain.replace("*", "_");
@@ -52,7 +52,17 @@ public class Certificate extends BaseTimeEntity {
     public void setDomain(String domain) {
     }
 
-    public void setDescription(String description) {
+    //    public void setDescription(String description) {
+//
+//    }
+    @Transient
+    private String apiToken;
 
+    public void setExpiredAt(LocalDateTime localDateTime) {
+
+    }
+
+    public void setEmail(String email) {
     }
 }
+
diff --git a/src/main/java/com/aolda/itda/repository/certificate/CertificateRepository.java b/src/main/java/com/aolda/itda/repository/certificate/CertificateRepository.java
index 1ce5c1fa17c006138df409444d9476bdd8a9b3fa..b8cc1489734eb787564f6bd7677ea34e7a4161e9 100644
--- a/src/main/java/com/aolda/itda/repository/certificate/CertificateRepository.java
+++ b/src/main/java/com/aolda/itda/repository/certificate/CertificateRepository.java
@@ -4,6 +4,7 @@ import com.aolda.itda.entity.certificate.Certificate;
 import com.aolda.itda.entity.forwarding.Forwarding;
 import org.springframework.data.jpa.repository.JpaRepository;
 
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Optional;
 
@@ -15,4 +16,14 @@ public interface CertificateRepository extends JpaRepository<Certificate, Long>
     // 프로젝트별 목록 조회 (Soft Delete 고려)
     List<Certificate> findByProjectIdAndIsDeleted(String projectId, Boolean isDeleted);
 
+    // 만료일이 주어진 날짜 이전인(=만료 30일 이내) 인증서 조회
+    //List<Certificate> findByExpiredAtBeforeAndIsDeleted(LocalDateTime date, Boolean isDeleted);
+
+    // 1) domain 필터링용 메서드
+    List<Certificate> findByProjectIdAndDomainContainingIgnoreCaseAndIsDeleted(
+            String projectId, String domain, Boolean isDeleted);
+
+    // 3) 만료 30일 이내 대상 조회
+    List<Certificate> findByExpiredAtBeforeAndIsDeleted(
+            LocalDateTime date, Boolean isDeleted);
 }
diff --git a/src/main/java/com/aolda/itda/service/certificate/CertificateService.java b/src/main/java/com/aolda/itda/service/certificate/CertificateService.java
index f6f83bbcf832ef929fd52709477ef1976657f1fd..92d4c835b9f93f38506230bd2cd1a89a4662e98f 100644
--- a/src/main/java/com/aolda/itda/service/certificate/CertificateService.java
+++ b/src/main/java/com/aolda/itda/service/certificate/CertificateService.java
@@ -12,9 +12,14 @@ import jakarta.validation.ConstraintViolation;
 import jakarta.validation.Validation;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -27,193 +32,190 @@ public class CertificateService {
     private final CertificateRepository certificateRepository;
     private final AuthService authService;
 
-    /* 인증서 하나 조회 */
+    /** 1) 단건 조회 **/
     public CertificateDTO getCertificate(Long certificateId, List<String> projects) {
-        Certificate certificate = certificateRepository
+        Certificate cert = certificateRepository
                 .findByCertificateIdAndIsDeleted(certificateId, false)
                 .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_FORWARDING));
-        // 여기서는 ErrorCode.NOT_FOUND_CERTIFICATE 등 새로운 코드로 교체 가능
-
-        // 프로젝트 권한 검증
-        authService.validateProjectAuth(projects, certificate.getProjectId());
-
-        return toDTO(certificate);
+        authService.validateProjectAuth(projects, cert.getProjectId());
+        return toDTO(cert);
     }
 
-    /* 인증서 목록 조회 */
-    public PageResp<CertificateDTO> getCertificates(String projectId) {
-        List<CertificateDTO> list = certificateRepository
-                .findByProjectIdAndIsDeleted(projectId, false)
-                .stream()
+    /** 1) 목록 조회 (domain 필터 optional) **/
+    public PageResp<CertificateDTO> getCertificates(String projectId, String domain) {
+        List<Certificate> list;
+        if (domain != null && !domain.isBlank()) {
+            list = certificateRepository
+                    .findByProjectIdAndDomainContainingIgnoreCaseAndIsDeleted(
+                            projectId, domain, false);
+        } else {
+            list = certificateRepository
+                    .findByProjectIdAndIsDeleted(projectId, false);
+        }
+        List<CertificateDTO> dtos = list.stream()
                 .map(this::toDTO)
                 .toList();
-
         return PageResp.<CertificateDTO>builder()
-                .contents(list)
+                .contents(dtos)
                 .build();
     }
 
-    /* 인증서 생성 */
-    /*public CertificateDTO createCertificate(String projectId,
+    /** 2) 생성: expiredAt 자동 90일 설정 + 로깅 **/
+    public CertificateDTO createCertificate(String projectId,
                                             CertificateDTO dto,
                                             List<String> projects) {
-        // 프로젝트 권한 검증
-        authService.validateProjectAuth(projects, projectId);
-        System.out.println("2");
-        // DTO 유효성 검사
-        validateDTO(dto);
-
-        Certificate certificate = Certificate.builder()
-                .projectId(projectId)
-                .domain(dto.getDomain())
-                .description(dto.getDescription())
-                .isDeleted(false)
-                .build();
-
-        certificateRepository.save(certificate);
-        System.out.println("3");
-        // 생성 로직 (certbot 호출 등) 필요 시 추가
-
-        return toDTO(certificate);
-    }*/
-    /* 인증서 생성 + lego 호출 */
-    public CertificateDTO createCertificate(String projectId, CertificateDTO dto, List<String> projects) {
-
-        // 1) 권한 체크
+        log.info("createCertificate start (project={})", projectId);
         authService.validateProjectAuth(projects, projectId);
-
-        // 2) DTO 검증
         validateDTO(dto);
 
-        // 3) lego 명령어 구성
-        ProcessBuilder pb = buildLegoProcess(dto);
+        // 발급
+        executeLego(dto);
+        log.info("certificate issued for domain={}", dto.getDomain());
 
-        // 4) lego 실행
-        int exitCode;
-        try {
-            Process process = pb.start();
-            exitCode = process.waitFor();
-            if (exitCode != 0) {
-                String err = new String(process.getErrorStream().readAllBytes());
-                log.error("[lego-error] {}", err);
-                throw new CustomException(ErrorCode.FAIL_CREATE_CONF,
-                        "lego 오류: " + err);
-            }
-        } catch (Exception e) {
-            log.error("[lego-exec] {}", e.getMessage());
-            throw new CustomException(ErrorCode.FAIL_CREATE_CONF,
-                    "lego 실행 실패");
-        }
-
-        // 5) 엔티티 저장
-        Certificate certificate = Certificate.builder()
+        // 엔티티 저장 (expiredAt 기본 90일 뒤)
+        Certificate cert = Certificate.builder()
                 .projectId(projectId)
                 .domain(dto.getDomain())
                 .email(dto.getEmail())
                 .challenge(dto.getChallenge())
-                .expiredAt(dto.getExpiredAt())   // 필요 시 lego 출력 파싱
+                .expiredAt(LocalDateTime.now().plusDays(90))
                 .isDeleted(false)
-                .description(dto.getDescription())
+                .apiToken(dto.getApiToken())
                 .build();
+        certificateRepository.save(cert);
+        log.info("certificate saved (id={}, domain={})",
+                cert.getCertificateId(), cert.getDomain());
 
-        certificateRepository.save(certificate);
-        return toDTO(certificate);
+        return toDTO(cert);
     }
 
-    /* 인증서 수정  */
-    public CertificateDTO editCertificate(Long certificateId, CertificateDTO dto, List<String> projects) {
-        Certificate certificate = certificateRepository
+    /** 4) 수정 **/
+    public CertificateDTO editCertificate(Long certificateId,
+                                          CertificateDTO dto,
+                                          List<String> projects) {
+        Certificate cert = certificateRepository
                 .findByCertificateIdAndIsDeleted(certificateId, false)
                 .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_FORWARDING));
+        authService.validateProjectAuth(projects, cert.getProjectId());
 
-        // 프로젝트 권한 검증
-        authService.validateProjectAuth(projects, certificate.getProjectId());
-
-        // 필요한 필드만 수정
-        if (dto.getDomain() != null) {
-            certificate.setDomain(dto.getDomain());
-        }
-        if (dto.getDescription() != null) {
-            certificate.setDescription(dto.getDescription());
-        }
-        // 기타 수정할 필드가 있다면 추가
-
-        // DB 저장
-        certificateRepository.save(certificate);
+        if (dto.getDomain() != null)      cert.setDomain(dto.getDomain());
+        if (dto.getEmail() != null)       cert.setEmail(dto.getEmail());
 
-        // 수정 로직(certbot 재발급 등) 필요 시 추가
-
-        return toDTO(certificate);
+        certificateRepository.save(cert);
+        log.info("certificate edited (id={}, domain={})",
+                cert.getCertificateId(), cert.getDomain());
+        return toDTO(cert);
     }
 
-    /* 인증서 삭제 (soft delete) */
+    /** 4) 삭제 + 로깅 **/
     public void deleteCertificate(Long certificateId, List<String> projects) {
-        Certificate certificate = certificateRepository
+        Certificate cert = certificateRepository
                 .findByCertificateIdAndIsDeleted(certificateId, false)
                 .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_FORWARDING));
+        authService.validateProjectAuth(projects, cert.getProjectId());
 
-        // 권한 검증
-        authService.validateProjectAuth(projects, certificate.getProjectId());
-
-        // soft delete
-        certificate.setIsDeleted(true);
-        certificateRepository.save(certificate);
+        cert.setIsDeleted(true);
+        certificateRepository.save(cert);
+        log.info("certificate deleted (id={}, domain={})",
+                cert.getCertificateId(), cert.getDomain());
+    }
 
-        // (추가) 파일 제거 / certbot revoke 등 로직 필요 시
+    /** 3) 만료 30일 전 자동 갱신 배치 **/
+    @Scheduled(cron = "0 0 3 * * *", zone = "Asia/Seoul")
+    public void renewExpiringCertificates() {
+        LocalDateTime threshold = LocalDateTime.now().plusDays(30);
+        List<Certificate> expiring = certificateRepository
+                .findByExpiredAtBeforeAndIsDeleted(threshold, false);
+
+        for (Certificate cert : expiring) {
+            try {
+                log.info("renewing (id={}, domain={})", cert.getCertificateId(), cert.getDomain());
+                CertificateDTO dto = CertificateDTO.builder()
+                        .domain(cert.getDomain())
+                        .email(cert.getEmail())
+                        .challenge(cert.getChallenge())
+                        .apiToken(cert.getApiToken())
+                        .build();
+                executeLego(dto);
+
+                cert.setExpiredAt(LocalDateTime.now().plusDays(90));
+                certificateRepository.save(cert);
+                log.info("renewed (id={}, newExpiry={})",
+                        cert.getCertificateId(), cert.getExpiredAt());
+            } catch (Exception e) {
+                log.error("failed to renew (id={}, domain={}): {}",
+                        cert.getCertificateId(), cert.getDomain(), e.getMessage());
+            }
+        }
     }
 
-    /* DTO 유효성 검사 */
+    /** DTO 유효성 검사 **/
     private void validateDTO(CertificateDTO dto) {
-        for (ConstraintViolation<CertificateDTO> violation
-                : Validation.buildDefaultValidatorFactory().getValidator().validate(dto)) {
-            throw new CustomException(ErrorCode.INVALID_CONF_INPUT, violation.getMessage());
+        for (ConstraintViolation<CertificateDTO> v :
+                Validation.buildDefaultValidatorFactory()
+                        .getValidator().validate(dto)) {
+            throw new CustomException(ErrorCode.INVALID_CONF_INPUT, v.getMessage());
         }
     }
 
-    /* Entity -> DTO 변환 */
-    private CertificateDTO toDTO(Certificate certificate) {
+    /** Entity→DTO 변환 **/
+    private CertificateDTO toDTO(Certificate cert) {
         return CertificateDTO.builder()
-                .certificateId(certificate.getCertificateId())
-                .projectId(certificate.getProjectId())
-                .domain(certificate.getDomain())
-                .description(certificate.getDescription())
-                .isDeleted(certificate.getIsDeleted())
-                .expiredAt(certificate.getExpiredAt())
+                .certificateId(cert.getCertificateId())
+
+                .domain(cert.getDomain())
+                .email(cert.getEmail())
+                .challenge(cert.getChallenge())
+                .expiredAt(cert.getExpiredAt())
+                .createdAt(cert.getCreatedAt())
+                .updatedAt(cert.getUpdatedAt())
+                .isDeleted(cert.getIsDeleted())
+                .apiToken(cert.getApiToken())
                 .build();
     }
 
+    /** 인증서 발급용 lego 실행 **/
+    private void executeLego(CertificateDTO dto) {
+        if (dto.getChallenge() == Challenge.DNS_CLOUDFLARE && dto.getApiToken() == null) {
+            throw new CustomException(ErrorCode.INVALID_CONF_INPUT,
+                    "DNS_CLOUDFLARE 챌린지는 apiToken 필요");
+        }
 
-
-    /* lego ProcessBuilder 생성 */
-    private ProcessBuilder buildLegoProcess(CertificateDTO dto) {
-
-        String basePath = "/data/lego"; // 인증서 저장 루트(볼륨)
         List<String> cmd = new ArrayList<>();
         cmd.add("/usr/local/bin/lego");
         cmd.add("--accept-tos");
-        cmd.add("--email");        cmd.add(dto.getEmail());
-        cmd.add("--path");         cmd.add(basePath);
+        cmd.add("--email");   cmd.add(dto.getEmail());
+        cmd.add("--path");    cmd.add("/data/lego");
 
         if (dto.getChallenge() == Challenge.HTTP) {
             cmd.add("--http");
-            cmd.add("--http.webroot");
-            cmd.add("/data/letsencrypt-acme-challenge");
-        } else if (dto.getChallenge() == Challenge.DNS_CLOUDFLARE) {
-            cmd.add("--dns");
-            cmd.add("cloudflare");
-            // CLOUDFLARE_API_TOKEN 환경변수를 컨테이너에 세팅했다고 가정
+            cmd.add("--http.webroot"); cmd.add("/data/letsencrypt-acme-challenge");
+        } else {
+            cmd.add("--dns");  cmd.add("cloudflare");
         }
 
-        cmd.add("--domains");      cmd.add(dto.getDomain());
-        cmd.add("run");            // 최초 발급(run) / renew(갱신)
+        cmd.add("--domains"); cmd.add(dto.getDomain());
+        cmd.add("run");
 
-        return new ProcessBuilder(cmd)
+        log.info("executing lego: {}", String.join(" ", cmd));
+        ProcessBuilder pb = new ProcessBuilder(cmd)
                 .redirectErrorStream(true);
-    }
-}
-
+        pb.environment().put("CF_DNS_API_TOKEN", dto.getApiToken());
 
+        try {
+            Process p = pb.start();
+            try (BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
+                r.lines().forEach(line -> log.info("[lego] {}", line));
+            }
+            if (p.waitFor() != 0) {
+                throw new CustomException(ErrorCode.FAIL_CREATE_CERT,
+                        "lego exit code=" + p.exitValue());
+            }
+        } catch (IOException | InterruptedException e) {
+            log.error("lego error", e);
+            throw new CustomException(ErrorCode.FAIL_CREATE_CERT,
+                    "lego 실행 실패: " + e.getMessage());
+        }
+    }
 
-// 여기서 매소드를 create로 해서 lego --accept-tos --email "email@example.com" --http --http.webroot data/letsencrypt-acme-challenge --path /data/lego --domains www.example.com run
-// 이거 이메일 . 도메인 으로 넣어서 실제 인증서 연동하기!!!
-// 그리고 Dto에 관리자 이메일, 인증서 완료일, 챌린지 방식 추가하기
\ No newline at end of file
+}