Repository: kim946509/sequence-three
Pull request #24: 프론트 요구사항 수정, request, response, 엑세스 토큰 등...
Author: login: Jaeboong
Created: 2025-03-05T16:49:33Z | Merged: 2025-03-07T10:48:00Z

status request response에서 삭제
period request response에서 삭제
멤버 추가됨
사진은 규원쪽에서는 MINIO라는 서버에 저장하는 방식을 사용중인데 규원이랑 얘기해보고 이것도 아마 비슷한 방식으로 처리 예정
기간을 duration string 에서 startDate, endDate로 분리하였고 LocalDate타입으로 수정
아카이브 등록,조회,수정에도 엑세스토큰 사용하도록 수정


Diff:
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index a9aeb33..f52eec4 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -40,6 +40,9 @@ jobs:
       JWT_SECRET: ${{ secrets.JWT_SECRET }}
       MAX_FILE_SIZE: ${{ secrets.MAX_FILE_SIZE }}
       MAX_REQUEST_SIZE : ${{ secrets.MAX_REQUEST_SIZE }}
+      MINIO_ENDPOINT: ${{secrets.MINIO_ENDPOINT}}
+      MINIO_ACCESS_KEY: ${{secrets.MINIO_ACCESS_KEY}}
+      MINIO_SECRET_KEY: ${{secrets.MINIO_SECRET_KEY}}
 
     
     steps:
@@ -50,6 +53,7 @@ jobs:
         java-version: '17'
         distribution: 'temurin'
 
+
     # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
     # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md
     - name: Setup Gradle
@@ -61,6 +65,7 @@ jobs:
       run: ./gradlew build
       working-directory: sequence_member
 
+
     # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html).
     # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version.
     #
@@ -72,6 +77,28 @@ jobs:
     # - name: Build with Gradle 8.9
     #   run: gradle build
 
+    # 도커 로그인
+    - name: Docker Login
+      uses: docker/login-action@v3.3.0
+      with:
+        username: ${{ secrets.DOCKER_USERNAME }}
+        password: ${{ secrets.DOCKER_PASSWORD }}
+
+    - name: List repository root
+      run: ls -la
+    - name: List sequence_member directory
+      run: ls -la sequence_member
+    # Docker 이미지 빌드
+    - name: Build Docker image
+      run: |
+        docker build -t ${{secrets.DOCKER_USERNAME}}/sequence:latest .
+      working-directory: sequence_member
+    # Docker 이미지 푸시
+    - name: Push Docker image
+      run: |
+        docker push ${{secrets.DOCKER_USERNAME}}/sequence:latest
+      working-directory: sequence_member
+
   dependency-submission:
     runs-on: ubuntu-latest
     permissions:
@@ -91,25 +118,37 @@ jobs:
       uses: gradle/actions/dependency-submission@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
       with:
         build-root-directory: sequence_member
-
-
-  docker-build-and-push:
-    needs: [build, dependency-submission] #위에 작업 완료후 진행
+  
+  # ssh연결 및 실행
+  deploy:
+    needs: [build]
     runs-on: ubuntu-latest
     steps:
-      # 도커 로그인
-      - name: Docker Login
-        uses: docker/login-action@v3.3.0
+      - name: Deploy to Server via SSH
+        uses: appleboy/ssh-action@v0.1.8
         with:
-          username: ${{ secrets.DOCKER_USERNAME }}
-          password: ${{ secrets.DOCKER_PASSWORD }}
-      # Docker 이미지 빌드
-      - name: Build Docker image
-        run: |
-          docker build -t ${{secrets.DOCKER_USERNAME}}/sequence:latest .
-        working-directory: sequence_member
-      # Docker 이미지 푸시
-      - name: Push Docker image
-        run: |
-          docker push ${{secrets.DOCKER_USERNAME}}/sequence:lates
-        
+          host: ${{secrets.SERVER_HOST}}
+          username: ${{secrets.SERVER_USERNAME}}
+          key: ${{secrets.SERVER_SSH_KEY}}
+          port: 22
+          script: |
+            # 서버에서 최신 이미지 pull
+            docker pull ${{secrets.DOCKER_USERNAME}}/sequence:latest
+            # 기존 컨테이너 종료 및 삭제 (존재하는 경우)
+            docker stop sequence-spring-container || true
+            docker rm sequence-spring-container || true
+            # 새로운 컨테이너 실행 (포트 및 필요한 환경 변수 지정 가능)
+            docker run -d --name sequence-spring-container --network sequence -p 10101:8080 \
+              -e JWT_SECRET=${{secrets.JWT_SECRET}} \
+              -e MAX_FILE_SIZE=${{secrets.MAX_FILE_SIZE}} \
+              -e MAX_REQUEST_SIZE=${{secrets.MAX_REQUEST_SIZE}} \
+              -e DATABASE_DRIVER=org.mariadb.jdbc.Driver \
+              -e DATABASE_URL=${{secrets.DATABASE_URL}} \
+              -e DATABASE_USERNAME=${{secrets.DATABASE_USERNAME}} \
+              -e DATABASE_PASSWORD=${{secrets.DATABASE_PASSWORD}} \
+              -e MINIO_ENDPOINT=${{secrets.MINIO_ENDPOINT}} \
+              -e MINIO_ACCESS_KEY=${{secrets.MINIO_ACCESS_KEY}} \
+              -e MINIO_SECRET_KEY=${{secrets.MINIO_SECRET_KEY}} \
+              ${{secrets.DOCKER_USERNAME}}/sequence:latest
+
+      
diff --git a/sequence_member/Dockerfile b/sequence_member/Dockerfile
index 7750684..ece6de4 100644
--- a/sequence_member/Dockerfile
+++ b/sequence_member/Dockerfile
@@ -1,5 +1,5 @@
 FROM openjdk:17-jdk-slim
 WORKDIR /app
-COPY sequence_member/build/libs/sequence_member-0.0.1-SNAPSHOT.jar app.jar
+COPY build/libs/sequence_member-0.0.1-SNAPSHOT.jar app.jar
 ENV TZ=Asia/Seoul
 ENTRYPOINT ["java", "-jar", "app.jar"]
\ No newline at end of file
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/controller/ArchiveController.java b/sequence_member/src/main/java/sequence/sequence_member/archive/controller/ArchiveController.java
index adc9690..8bc8419 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/archive/controller/ArchiveController.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/controller/ArchiveController.java
@@ -3,6 +3,7 @@
 import jakarta.validation.Valid;
 import lombok.RequiredArgsConstructor;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import sequence.sequence_member.archive.dto.ArchiveOutputDTO;
@@ -14,6 +15,7 @@
 import sequence.sequence_member.global.enums.enums.SortType;
 import sequence.sequence_member.global.response.ApiResponseData;
 import sequence.sequence_member.global.response.Code;
+import sequence.sequence_member.member.dto.CustomUserDetails;
 
 @RestController
 @RequiredArgsConstructor
@@ -24,67 +26,71 @@ public class ArchiveController {
 
     // 아카이브 등록
     @PostMapping
-    public ResponseEntity<ApiResponseData<String>> createArchive(@Valid @RequestBody ArchiveRegisterInputDTO archiveRegisterInputDTO) {
-        archiveService.createArchive(archiveRegisterInputDTO);
+    public ResponseEntity<ApiResponseData<String>> createArchive(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @Valid @RequestBody ArchiveRegisterInputDTO archiveRegisterInputDTO) {
+        archiveService.createArchive(archiveRegisterInputDTO, userDetails.getUsername());
         return ResponseEntity.ok(ApiResponseData.success(null, "아카이브 등록 성공"));
     }
 
     // 아카이브 등록 후 결과 조회
     @GetMapping("/{archiveId}")
-    public ResponseEntity<ApiResponseData<ArchiveOutputDTO>> getArchiveById(@PathVariable("archiveId") Long archiveId) {
-        return ResponseEntity.ok().body(ApiResponseData.of(Code.SUCCESS.getCode(), "아카이브 상세 조회 성공", archiveService.getArchiveById(archiveId)));
+    public ResponseEntity<ApiResponseData<ArchiveOutputDTO>> getArchiveById(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @PathVariable("archiveId") Long archiveId) {
+        return ResponseEntity.ok().body(ApiResponseData.of(
+            Code.SUCCESS.getCode(), 
+            "아카이브 상세 조회 성공", 
+            archiveService.getArchiveById(archiveId, userDetails.getUsername())
+        ));
     }
 
     // 아카이브 수정
     @PutMapping("/{archiveId}")
-    public ResponseEntity<ApiResponseData<String>> updateArchive(@PathVariable("archiveId") Long archiveId,
-                                                                 @Valid @RequestBody ArchiveUpdateDTO archiveUpdateDTO) {
-        archiveService.updateArchive(archiveId, archiveUpdateDTO);
+    public ResponseEntity<ApiResponseData<String>> updateArchive(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @PathVariable("archiveId") Long archiveId,
+            @Valid @RequestBody ArchiveUpdateDTO archiveUpdateDTO) {
+        archiveService.updateArchive(archiveId, archiveUpdateDTO, userDetails.getUsername());
         return ResponseEntity.ok(ApiResponseData.success(null, "아카이브 수정 성공"));
     }
 
-    // 아카이브 삭제
+    // 아카이브 삭제 // 
     @DeleteMapping("/{archiveId}")
-    public ResponseEntity<ApiResponseData<String>> deleteArchiveById(@PathVariable("archiveId") Long archiveId) {
-        archiveService.deleteArchive(archiveId);
+    public ResponseEntity<ApiResponseData<String>> deleteArchiveById(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @PathVariable("archiveId") Long archiveId) {
+        archiveService.deleteArchive(archiveId, userDetails.getUsername());
         return ResponseEntity.ok(ApiResponseData.success(null, "아카이브 삭제 성공"));
     }
 
     // 전체 리스트 조회
     @GetMapping("/projects")
     public ResponseEntity<ApiResponseData<ArchivePageResponseDTO>> getArchiveList(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
             @RequestParam(defaultValue = "0") int page,
             @RequestParam(defaultValue = "LATEST") SortType sortType) {
         
         return ResponseEntity.ok().body(ApiResponseData.of(
                 Code.SUCCESS.getCode(),
                 "아카이브 프로젝트 리스트 조회에 성공했습니다.",
-                archiveService.getAllArchives(page, sortType)
+                archiveService.getAllArchives(page, sortType, userDetails.getUsername())
         ));
     }
 
     // 검색 (카테고리 또는 키워드)
     @GetMapping("/projects/search")
     public ResponseEntity<ApiResponseData<ArchivePageResponseDTO>> searchArchives(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
             @RequestParam(defaultValue = "0") int page,
             @RequestParam(required = false) Category category,
             @RequestParam(required = false) String keyword,
             @RequestParam(defaultValue = "LATEST") SortType sortType) {
         
-        ArchivePageResponseDTO response;
-        
-        if (category != null) {
-            response = archiveService.searchByCategory(category, page, sortType);
-        } else if (keyword != null && !keyword.trim().isEmpty()) {
-            response = archiveService.searchByTitle(keyword, page, sortType);
-        } else {
-            response = archiveService.getAllArchives(page, sortType);
-        }
-        
         return ResponseEntity.ok().body(ApiResponseData.of(
                 Code.SUCCESS.getCode(),
                 "검색 결과를 성공적으로 조회했습니다.",
-                response
+                archiveService.searchArchives(category, keyword, page, sortType, userDetails.getUsername())
         ));
     }
 } 
\ No newline at end of file
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/controller/TeamEvaluationController.java b/sequence_member/src/main/java/sequence/sequence_member/archive/controller/TeamEvaluationController.java
new file mode 100644
index 0000000..29d1d00
--- /dev/null
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/controller/TeamEvaluationController.java
@@ -0,0 +1,70 @@
+package sequence.sequence_member.archive.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import sequence.sequence_member.archive.dto.TeamEvaluationRequestDto;
+import sequence.sequence_member.archive.entity.TeamEvaluation;
+import sequence.sequence_member.archive.service.TeamEvaluationService;
+import sequence.sequence_member.global.response.ApiResponseData;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import sequence.sequence_member.member.dto.CustomUserDetails;
+import org.springframework.security.core.context.SecurityContextHolder;
+import sequence.sequence_member.global.response.Code;
+import sequence.sequence_member.global.enums.enums.Status;
+import sequence.sequence_member.archive.dto.TeamEvaluationResponseDto;
+
+import jakarta.validation.Valid;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/archive")
+public class TeamEvaluationController {
+
+    private final TeamEvaluationService teamEvaluationService;
+
+    @PostMapping("/{archiveId}/evaluations")
+    public ResponseEntity<?> createTeamEvaluation(
+            @PathVariable Long archiveId,
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @Valid @RequestBody TeamEvaluationRequestDto requestDto) {
+            
+        teamEvaluationService.createTeamEvaluation(archiveId, userDetails.getUsername(), requestDto);
+        return ResponseEntity
+                .status(Code.CREATED.getStatus())
+                .body(ApiResponseData.of(Code.CREATED.getCode(), "팀원 평가가 완료되었습니다.", null));
+    }
+
+    @GetMapping("/{archiveId}/evaluations")
+    public ResponseEntity<?> getTeamEvaluations(
+            @PathVariable Long archiveId,
+            @AuthenticationPrincipal CustomUserDetails userDetails) {
+    
+        List<TeamEvaluationResponseDto> evaluations = teamEvaluationService.getTeamEvaluations(archiveId, userDetails.getUsername());
+        return ResponseEntity.ok(ApiResponseData.success(evaluations));
+    }
+
+    @GetMapping(
+        value = "/{archiveId}/evaluations/status",
+        produces = "application/json"
+    )
+    public ResponseEntity<ApiResponseData<Map<String, Status>>> getEvaluationStatus(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @PathVariable Long archiveId) {
+        Map<String, Status> evaluationStatus = teamEvaluationService.getEvaluationStatus(archiveId, userDetails.getUsername());
+        return ResponseEntity.ok(ApiResponseData.success(evaluationStatus));
+    }
+
+    @GetMapping("/{archiveId}/team-members")
+    public ResponseEntity<ApiResponseData<List<String>>> getTeamMembers(
+            @PathVariable Long archiveId) {
+        // 팀원의 목록을 가져오는 서비스 호출 (teamEvaluationService가 아닌 다른 서비스일 수 있음)
+        List<String> evaluators = teamEvaluationService.getEvaluators(archiveId);
+        return ResponseEntity.ok(ApiResponseData.success(evaluators));
+    }
+} 
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveOutputDTO.java b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveOutputDTO.java
index 54fc3ff..6315835 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveOutputDTO.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveOutputDTO.java
@@ -5,9 +5,8 @@
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import sequence.sequence_member.global.enums.enums.Category;
-import sequence.sequence_member.global.enums.enums.Period;
-import sequence.sequence_member.global.enums.enums.Status;
 
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.util.List;
 
@@ -18,17 +17,28 @@
 public class ArchiveOutputDTO {
     private Long id;
     private String title;
-    private String description;
+    private String description; 
+    private LocalDate startDate;
+    private LocalDate endDate;
     private String duration;
     private Category category;
-    private Period period;
-    private Status status;
     private String thumbnail;
     private String link;
     private List<String> skills;
     private List<String> imgUrls;
     private Integer view;
     private Integer bookmark;
+    private List<ArchiveMemberDTO> members;
     private LocalDateTime createdDateTime;
     private LocalDateTime modifiedDateTime;
-} 
\ No newline at end of file
+
+    @Getter
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class ArchiveMemberDTO {
+        private String username;
+        private String nickname;
+        private String role;
+    }
+} 
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveRegisterInputDTO.java b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveRegisterInputDTO.java
index 13d12af..870d208 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveRegisterInputDTO.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveRegisterInputDTO.java
@@ -9,15 +9,14 @@
 import lombok.Builder;
 import org.hibernate.validator.constraints.Length;
 import sequence.sequence_member.global.enums.enums.Category;
-import sequence.sequence_member.global.enums.enums.Period;
-import sequence.sequence_member.global.enums.enums.Status;
 
 import java.util.List;
+import java.time.LocalDate;
 
 @Getter
-@Builder
 @NoArgsConstructor
 @AllArgsConstructor
+@Builder
 public class ArchiveRegisterInputDTO {
 
     @NotEmpty(message = "제목을 입력해주세요.")
@@ -28,17 +27,15 @@ public class ArchiveRegisterInputDTO {
     @Length(min = 1, max = 450, message = "설명은 450자 이하로 입력해주세요.")
     private String description;
 
-    @NotEmpty(message = "기간을 입력해주세요.")
-    private String duration;
+    @NotNull(message = "시작일을 입력해주세요.")
+    private LocalDate startDate;
+
+    @NotNull(message = "종료일을 입력해주세요.")
+    private LocalDate endDate;
 
     @NotNull(message = "카테고리를 선택해주세요.")
     private Category category;
 
-    @NotNull(message = "기간을 선택해주세요.")
-    private Period period;
-
-    @NotNull(message = "상태를 선택해주세요.")
-    private Status status;
 
     private String thumbnail;
     private String link;
@@ -46,5 +43,15 @@ public class ArchiveRegisterInputDTO {
     @NotEmpty(message = "관련 기술을 선택해주세요.")
     @Size(max = 20, message = "관련 기술은 20개 이하로 선택해주세요.")
     private List<String> skills;
+
+    private List<ArchiveMemberDTO> archiveMembers;
+
+    @Getter
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class ArchiveMemberDTO {
+        private String username;
+        private String role;
+    }
 }
 
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveUpdateDTO.java b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveUpdateDTO.java
index 624f3c6..7d8c14a 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveUpdateDTO.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveUpdateDTO.java
@@ -6,9 +6,8 @@
 import lombok.*;
 import org.hibernate.validator.constraints.Length;
 import sequence.sequence_member.global.enums.enums.Category;
-import sequence.sequence_member.global.enums.enums.Period;
-import sequence.sequence_member.global.enums.enums.Status;
 
+import java.time.LocalDate;
 import java.util.List;
 
 @Getter
@@ -21,22 +20,32 @@ public class ArchiveUpdateDTO {
     @Length(min = 1, max = 450, message = "설명은 450자 이하로 입력해주세요.")
     private String description;
 
-    @NotEmpty(message = "기간을 입력해주세요.")
-    private String duration;
+    @NotNull(message = "시작일을 입력해주세요.")
+    private LocalDate startDate;
+
+    @NotNull(message = "종료일을 입력해주세요.")
+    private LocalDate endDate;
 
     @NotNull(message = "카테고리를 선택해주세요.")
     private Category category;
 
-    @NotNull(message = "기간을 선택해주세요.")
-    private Period period;
-
-    @NotNull(message = "상태를 선택해주세요.")
-    private Status status;
-
     private String thumbnail;
     private String link;
 
     @NotEmpty(message = "관련 기술을 선택해주세요.")
     @Size(max = 20, message = "관련 기술은 20개 이하로 선택해주세요.")
     private List<String> skills;
+
+    private List<String> imgUrls;
+
+    @NotEmpty(message = "팀원 정보를 입력해주세요.")
+    private List<ArchiveMemberDTO> archiveMembers;
+
+    @Getter
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class ArchiveMemberDTO {
+        private String username;
+        private String role;
+    }
 }
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/TeamEvaluationRequestDto.java b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/TeamEvaluationRequestDto.java
new file mode 100644
index 0000000..6ea5659
--- /dev/null
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/TeamEvaluationRequestDto.java
@@ -0,0 +1,34 @@
+package sequence.sequence_member.archive.dto;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import java.util.List;
+import jakarta.validation.Valid;
+
+@Getter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TeamEvaluationRequestDto {
+    @NotNull(message = "평가 목록은 필수입니다.")
+    @Valid
+    private List<EvaluationItem> evaluations;
+
+    @Getter
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class EvaluationItem {
+        @NotNull(message = "평가 받는 팀원의 username은 필수입니다.")
+        private String evaluatedUserName;
+        
+        @NotNull(message = "피드백 내용은 필수입니다.")
+        private String feedback;
+        
+        @NotNull(message = "키워드는 필수입니다.")
+        private List<String> keyword;
+    }
+} 
\ No newline at end of file
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/dto/TeamEvaluationResponseDto.java b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/TeamEvaluationResponseDto.java
new file mode 100644
index 0000000..bdd718e
--- /dev/null
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/dto/TeamEvaluationResponseDto.java
@@ -0,0 +1,52 @@
+package sequence.sequence_member.archive.dto;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.AllArgsConstructor;
+import sequence.sequence_member.archive.entity.TeamEvaluation;
+
+@Getter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TeamEvaluationResponseDto {
+    private EvaluatorInfo evaluator;
+    private List<EvaluatedInfo> evaluated;
+
+    @Getter
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class EvaluatorInfo {
+        private String username;
+        private String profileImg;
+    }
+
+    @Getter
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class EvaluatedInfo {
+        private String username;
+        private String profileImg;
+    }
+
+    public static TeamEvaluationResponseDto from(TeamEvaluation evaluation) {
+        return TeamEvaluationResponseDto.builder()
+                .evaluator(EvaluatorInfo.builder()
+                    .username(evaluation.getEvaluator().getMember().getUsername())
+                    .profileImg(evaluation.getEvaluator().getMember().getProfileImg())
+                    .build())
+                .evaluated((List<EvaluatedInfo>) EvaluatedInfo.builder()
+                    .username(evaluation.getEvaluated().getMember().getUsername())
+                    .profileImg(evaluation.getEvaluated().getMember().getProfileImg())
+                    .build())
+                .build();
+    }
+} 
\ No newline at end of file
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/entity/Archive.java b/sequence_member/src/main/java/sequence/sequence_member/archive/entity/Archive.java
index f1dcef3..8d48bb1 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/archive/entity/Archive.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/entity/Archive.java
@@ -22,11 +22,12 @@
 import lombok.Builder;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
-import sequence.sequence_member.archive.dto.ArchiveUpdateDTO;
 import sequence.sequence_member.global.enums.enums.Category;
-import sequence.sequence_member.global.enums.enums.Period;
 import sequence.sequence_member.global.enums.enums.Status;
 import sequence.sequence_member.global.utils.BaseTimeEntity;
+import sequence.sequence_member.archive.dto.ArchiveUpdateDTO;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 
 @Entity
 @Getter
@@ -47,15 +48,14 @@ public class Archive extends BaseTimeEntity {
     private String description;    
 
     @Column(nullable = false)
-    private String duration;       
+    private LocalDate startDate;  // 시작일
 
-    @Enumerated(EnumType.STRING)
     @Column(nullable = false)
-    private Category category;     // Category enum 사용
+    private LocalDate endDate;    // 종료일
 
     @Enumerated(EnumType.STRING)
     @Column(nullable = false)
-    private Period period;         // Period enum 사용
+    private Category category;     // Category enum 사용
 
     @Enumerated(EnumType.STRING)
     @Column(nullable = false)
@@ -115,15 +115,22 @@ public void setImageUrlsFromList(List<String> imageUrlList) {
         this.imgUrl = String.join(",", imageUrlList);
     }
 
-    // 아카이브 수정 시 사용
+    // duration String 대신 날짜 기간을 반환하는 메서드
+    public String getDurationAsString() {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM");
+        return startDate.format(formatter) + " ~ " + endDate.format(formatter);
+    }
+
+    // 아카이브 업데이트 메서드 수정
     public void updateArchive(ArchiveUpdateDTO archiveUpdateDTO) {
         this.title = archiveUpdateDTO.getTitle();
         this.description = archiveUpdateDTO.getDescription();
-        this.duration = archiveUpdateDTO.getDuration();
+        this.startDate = archiveUpdateDTO.getStartDate();
+        this.endDate = archiveUpdateDTO.getEndDate();
         this.category = archiveUpdateDTO.getCategory();
-        this.period = archiveUpdateDTO.getPeriod();
-        this.status = archiveUpdateDTO.getStatus();
         this.thumbnail = archiveUpdateDTO.getThumbnail();
         this.link = archiveUpdateDTO.getLink();
+        setSkillsFromList(archiveUpdateDTO.getSkills());
+        setImageUrlsFromList(archiveUpdateDTO.getImgUrls());
     }
-}
+} 
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/entity/TeamEvaluation.java b/sequence_member/src/main/java/sequence/sequence_member/archive/entity/TeamEvaluation.java
new file mode 100644
index 0000000..ce689e3
--- /dev/null
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/entity/TeamEvaluation.java
@@ -0,0 +1,53 @@
+package sequence.sequence_member.archive.entity;
+
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import sequence.sequence_member.global.utils.BaseTimeEntity;
+
+@Entity
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@AllArgsConstructor
+@Builder
+@Table(name = "team_evaluation")
+public class TeamEvaluation extends BaseTimeEntity {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "evaluator_id", nullable = false)
+    private ArchiveMember evaluator;    // 평가자
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "evaluated_id", nullable = false)
+    private ArchiveMember evaluated;    // 피평가자
+
+    @Column(nullable = true)
+    private String feedback;            // 피드백 내용
+
+    @Column(columnDefinition = "json", nullable = true)
+    private String keyword;             // 키워드 (JSON 형태로 저장)
+
+    @Column(nullable = true)
+    private String lineFeedback;    // 한줄 피드백 필드 추가
+
+    // 같은 Archive 내의 멤버끼리만 평가할 수 있도록 검증
+    public boolean validateSameArchive() {
+        return evaluator.getArchive().getId().equals(evaluated.getArchive().getId());
+    }
+
+    // 자기 자신을 평가할 수 없도록 검증
+    public boolean validateSelfEvaluation() {
+        return !evaluator.getId().equals(evaluated.getId());
+    }
+
+    public String getLineFeedback() {
+        return this.lineFeedback;
+    }
+} 
\ No newline at end of file
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/repository/ArchiveMemberRepository.java b/sequence_member/src/main/java/sequence/sequence_member/archive/repository/ArchiveMemberRepository.java
new file mode 100644
index 0000000..98e99aa
--- /dev/null
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/repository/ArchiveMemberRepository.java
@@ -0,0 +1,19 @@
+package sequence.sequence_member.archive.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import sequence.sequence_member.archive.entity.ArchiveMember;
+import sequence.sequence_member.archive.entity.Archive;
+import sequence.sequence_member.member.entity.MemberEntity;
+import java.util.List;
+
+public interface ArchiveMemberRepository extends JpaRepository<ArchiveMember, Long> {
+    // 기본적인 CRUD 메서드는 JpaRepository에서 제공됨
+
+    // 아카이브와 멤버 ID로 아카이브 멤버 찾기
+    ArchiveMember findByArchiveAndMemberId(Archive archive, Long memberId);
+
+    // 멤버와 아카이브 ID로 아카이브 멤버 찾기
+    ArchiveMember findByMemberAndArchive_Id(MemberEntity member, Long archiveId);
+
+    List<ArchiveMember> findAllByArchiveId(Long archiveId);
+} 
\ No newline at end of file
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/repository/ArchiveRepository.java b/sequence_member/src/main/java/sequence/sequence_member/archive/repository/ArchiveRepository.java
index b8ea7c2..b36043a 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/archive/repository/ArchiveRepository.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/repository/ArchiveRepository.java
@@ -13,11 +13,14 @@ public interface ArchiveRepository extends JpaRepository<Archive, Long> {
     //아카이브 등록 후 Read
     Optional<Archive> findById(Long id);
     
-    // 카테고리별 검색 (페이지네이션)
+    // 카테고리와 제목으로 검색
+    Page<Archive> findByCategoryAndTitleContainingIgnoreCase(Category category, String title, Pageable pageable);
+    
+    // 카테고리로만 검색
     Page<Archive> findByCategory(Category category, Pageable pageable);
     
-    // 제목 검색 (페이지네이션)
-    Page<Archive> findByTitleContaining(String keyword, Pageable pageable);
+    // 제목으로만 검색
+    Page<Archive> findByTitleContainingIgnoreCase(String title, Pageable pageable);
 
     
     // 전체 목록 조회는 JpaRepository의 findAll(Pageable) 사용
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/repository/TeamEvaluationRepository.java b/sequence_member/src/main/java/sequence/sequence_member/archive/repository/TeamEvaluationRepository.java
new file mode 100644
index 0000000..c92aed9
--- /dev/null
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/repository/TeamEvaluationRepository.java
@@ -0,0 +1,35 @@
+package sequence.sequence_member.archive.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import sequence.sequence_member.archive.entity.Archive;
+import sequence.sequence_member.archive.entity.ArchiveMember;
+import sequence.sequence_member.archive.entity.TeamEvaluation;
+
+import java.util.List;
+
+public interface TeamEvaluationRepository extends JpaRepository<TeamEvaluation, Long> {
+    
+    // 평가자가 특정 피평가자를 평가했는지 확인
+    boolean existsByEvaluatorAndEvaluated(ArchiveMember evaluator, ArchiveMember evaluated);
+    
+    // 특정 아카이브의 모든 팀원 간 상호평가가 완료되었는지 확인
+    @Query("SELECT COUNT(te) = " +
+           "(SELECT COUNT(am1) * (COUNT(am2) - 1) FROM ArchiveMember am1, ArchiveMember am2 " +
+           "WHERE am1.archive = :archive AND am2.archive = :archive AND am1 != am2) " +
+           "FROM TeamEvaluation te " +
+           "WHERE te.evaluator.archive = :archive")
+    boolean isAllEvaluationCompletedInArchive(@Param("archive") Archive archive);
+    
+    // 평가자와 피평가자로 평가 정보 조회
+    TeamEvaluation findByEvaluatorAndEvaluated(ArchiveMember evaluator, ArchiveMember evaluated);
+    
+    // 특정 멤버가 받은 평가 수 조회
+    long countByEvaluated(ArchiveMember evaluated);
+
+    @Query("SELECT DISTINCT t.evaluator FROM TeamEvaluation t WHERE t.evaluator.archive = :archive")
+    List<ArchiveMember> findDistinctEvaluatorsByArchive(Archive archive);
+
+    List<TeamEvaluation> findAllByEvaluatorAndEvaluator_Archive_Id(ArchiveMember evaluator, Long archiveId);
+} 
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/service/ArchiveService.java b/sequence_member/src/main/java/sequence/sequence_member/archive/service/ArchiveService.java
index be4db25..7d327c9 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/archive/service/ArchiveService.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/service/ArchiveService.java
@@ -16,6 +16,16 @@
 import sequence.sequence_member.global.enums.enums.Category;
 import sequence.sequence_member.global.enums.enums.SortType;
 import sequence.sequence_member.global.exception.CanNotFindResourceException;
+import sequence.sequence_member.member.entity.MemberEntity;
+import sequence.sequence_member.member.repository.MemberRepository;
+import sequence.sequence_member.archive.entity.ArchiveMember;
+import sequence.sequence_member.archive.repository.ArchiveMemberRepository;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.server.ResponseStatusException;
+import sequence.sequence_member.global.enums.enums.Status;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.springframework.web.server.ResponseStatusException;
 
 @Service
 @RequiredArgsConstructor
@@ -23,112 +33,133 @@
 public class ArchiveService {
     
     private final ArchiveRepository archiveRepository;
+    private final MemberRepository memberRepository;
+    private final ArchiveMemberRepository archiveMemberRepository;
 
     @Transactional
-    public ArchiveOutputDTO createArchive(ArchiveRegisterInputDTO archiveRegisterInputDTO) {
+    public ArchiveOutputDTO createArchive(ArchiveRegisterInputDTO dto, String username) {
+        // 사용자 검증
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "사용자를 찾을 수 없습니다."));
+
         Archive archive = Archive.builder()
-                .title(archiveRegisterInputDTO.getTitle())
-                .description(archiveRegisterInputDTO.getDescription())
-                .duration(archiveRegisterInputDTO.getDuration())
-                .category(archiveRegisterInputDTO.getCategory())
-                .period(archiveRegisterInputDTO.getPeriod())
-                .status(archiveRegisterInputDTO.getStatus())
-                .thumbnail(archiveRegisterInputDTO.getThumbnail())
-                .link(archiveRegisterInputDTO.getLink())
+                .title(dto.getTitle())
+                .description(dto.getDescription())
+                .startDate(dto.getStartDate())
+                .endDate(dto.getEndDate())
+                .category(dto.getCategory())
+                .status(Status.평가전)
+                .thumbnail(dto.getThumbnail())
+                .link(dto.getLink())
                 .build();
 
-        archive.setSkillsFromList(archiveRegisterInputDTO.getSkills());
-
+        archive.setSkillsFromList(dto.getSkills());
         Archive savedArchive = archiveRepository.save(archive);
 
-        return ArchiveOutputDTO.builder()
-                .id(savedArchive.getId())
-                .title(savedArchive.getTitle())
-                .description(savedArchive.getDescription())
-                .duration(savedArchive.getDuration())
-                .category(savedArchive.getCategory())
-                .period(savedArchive.getPeriod())
-                .status(savedArchive.getStatus())
-                .thumbnail(savedArchive.getThumbnail())
-                .link(savedArchive.getLink())
-                .skills(savedArchive.getSkillList())
-                .view(savedArchive.getView())
-                .bookmark(savedArchive.getBookmark())
-                .createdDateTime(savedArchive.getCreatedDateTime())
-                .modifiedDateTime(savedArchive.getModifiedDateTime())
+        // 아카이브 멤버 등록
+        for (ArchiveRegisterInputDTO.ArchiveMemberDTO memberDto : dto.getArchiveMembers()) {
+            MemberEntity archiveMember = memberRepository.findByUsername(memberDto.getUsername())
+                .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "존재하지 않는 사용자입니다: " + memberDto.getUsername()));
+
+            ArchiveMember newArchiveMember = ArchiveMember.builder()
+                .archive(savedArchive)
+                .member(archiveMember)
+                .role(memberDto.getRole())
                 .build();
+
+            archiveMemberRepository.save(newArchiveMember);
+        }
+
+        return convertToDTO(savedArchive);
     }
 
-    // 아카이브 등록 후 결과 조회
-    public ArchiveOutputDTO getArchiveById(Long archiveId) {
+    public ArchiveOutputDTO getArchiveById(Long archiveId, String username) {
+        // 사용자 검증
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "사용자를 찾을 수 없습니다."));
+
         Archive archive = archiveRepository.findById(archiveId)
-                .orElseThrow(() -> new IllegalArgumentException("해당 아카이브가 없습니다."));
-        return ArchiveOutputDTO.builder()
-                .id(archive.getId())
-                .title(archive.getTitle())
-                .description(archive.getDescription())
-                .duration(archive.getDuration())
-                .category(archive.getCategory())
-                .period(archive.getPeriod())
-                .status(archive.getStatus())
-                .thumbnail(archive.getThumbnail())
-                .link(archive.getLink())
-                .skills(archive.getSkillList())
-                .view(archive.getView())
-                .bookmark(archive.getBookmark())
-                .createdDateTime(archive.getCreatedDateTime())
-                .modifiedDateTime(archive.getModifiedDateTime())
-                .build();
+                .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "해당 아카이브가 없습니다."));
+        
+        return convertToDTO(archive);
     }
 
-    // 아카이브 내용 수정
     @Transactional
-    public void updateArchive(Long archiveId, ArchiveUpdateDTO archiveUpdateDTO) {
+    public void updateArchive(Long archiveId, ArchiveUpdateDTO archiveUpdateDTO, String username) {
+        // 사용자 검증
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "사용자를 찾을 수 없습니다."));
+
+        // 아카이브 멤버 검증
+        ArchiveMember archiveMember = archiveMemberRepository.findByMemberAndArchive_Id(member, archiveId);
+        if (archiveMember == null) {
+            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "해당 아카이브의 멤버가 아닙니다.");
+        }
+
         Archive archive = archiveRepository.findById(archiveId)
-                .orElseThrow(()-> new IllegalArgumentException("해당 아카이브가 없습니다."));
+                .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "해당 아카이브가 없습니다."));
         archive.updateArchive(archiveUpdateDTO);
     }
 
-    // 아카이브 삭제
     @Transactional
-    public void deleteArchive(Long archiveId) {
+    public void deleteArchive(Long archiveId, String username) {
+        // 사용자 검증
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "사용자를 찾을 수 없습니다."));
+
+        // 아카이브 멤버 검증
+        ArchiveMember archiveMember = archiveMemberRepository.findByMemberAndArchive_Id(member, archiveId);
+        if (archiveMember == null) {
+            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "해당 아카이브의 멤버가 아닙니다.");
+        }
+
         Archive archive = archiveRepository.findById(archiveId)
-                .orElseThrow(()-> new IllegalArgumentException("해당 아카이브가 없습니다."));
+                .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "해당 아카이브가 없습니다."));
         archiveRepository.delete(archive);
     }
 
-    
-    // 전체 아카이브 목록 조회 (정렬 추가)
-    public ArchivePageResponseDTO getAllArchives(int page, SortType sortType) {
+    public ArchivePageResponseDTO getAllArchives(int page, SortType sortType, String username) {
+        // 사용자 검증
+        memberRepository.findByUsername(username)
+            .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "사용자를 찾을 수 없습니다."));
+
         Pageable pageable = createPageableWithSort(page, sortType);
         Page<Archive> archivePage = archiveRepository.findAll(pageable);
         
         if(archivePage.isEmpty()) {
-            throw new CanNotFindResourceException("조건에 맞는 프로젝트를 찾을 수 없습니다.");
+            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "조건에 맞는 프로젝트를 찾을 수 없습니다.");
         }
         
         return createArchivePageResponse(archivePage);
     }
-    
-    // 카테고리별 아카이브 검색 (정렬 추가)
-    public ArchivePageResponseDTO searchByCategory(Category category, int page, SortType sortType) {
-        Pageable pageable = createPageableWithSort(page, sortType);
-        Page<Archive> archivePage = archiveRepository.findByCategory(category, pageable);
-        
-        if(archivePage.isEmpty()) {
-            throw new CanNotFindResourceException("조건에 맞는 프로젝트를 찾을 수 없습니다.");
-        }
+
+    public ArchivePageResponseDTO searchArchives(
+            Category category, 
+            String keyword, 
+            int page, 
+            SortType sortType, 
+            String username) {
         
-        return createArchivePageResponse(archivePage);
-    }
-    /* */
-    // 제목으로 아카이브 검색 (정렬 추가)
-    public ArchivePageResponseDTO searchByTitle(String keyword, int page, SortType sortType) {
+        // 사용자 검증
+        memberRepository.findByUsername(username)
+            .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "사용자를 찾을 수 없습니다."));
+
         Pageable pageable = createPageableWithSort(page, sortType);
-        Page<Archive> archivePage = archiveRepository.findByTitleContaining(keyword, pageable);
+        Page<Archive> archivePage;
         
+        // null 체크를 통한 메서드 선택
+        if (category != null && keyword != null && !keyword.trim().isEmpty()) {
+            archivePage = archiveRepository.findByCategoryAndTitleContainingIgnoreCase(category, keyword.trim(), pageable);
+        } else if (category != null) {
+            archivePage = archiveRepository.findByCategory(category, pageable);
+        } else if (keyword != null && !keyword.trim().isEmpty()) {
+            archivePage = archiveRepository.findByTitleContainingIgnoreCase(keyword.trim(), pageable);
+        } else {
+            archivePage = archiveRepository.findAll(pageable);
+        }
+
         if(archivePage.isEmpty()) {
-            throw new CanNotFindResourceException("조건에 맞는 프로젝트를 찾을 수 없습니다.");
+            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "조건에 맞는 프로젝트를 찾을 수 없습니다.");
         }
         
         return createArchivePageResponse(archivePage);
@@ -149,20 +180,29 @@ private Pageable createPageableWithSort(int page, SortType sortType) {
 
     // Archive 엔티티를 DTO로 변환
     private ArchiveOutputDTO convertToDTO(Archive archive) {
+        List<ArchiveOutputDTO.ArchiveMemberDTO> memberDTOs = archive.getArchiveMembers().stream()
+            .map(archiveMember -> ArchiveOutputDTO.ArchiveMemberDTO.builder()
+                .username(archiveMember.getMember().getUsername())
+                .nickname(archiveMember.getMember().getNickname())
+                .role(archiveMember.getRole())
+                .build())
+            .collect(Collectors.toList());
+
         return ArchiveOutputDTO.builder()
                 .id(archive.getId())
                 .title(archive.getTitle())
                 .description(archive.getDescription())
-                .duration(archive.getDuration())
+                .startDate(archive.getStartDate())
+                .endDate(archive.getEndDate())
+                .duration(archive.getDurationAsString())
                 .category(archive.getCategory())
-                .period(archive.getPeriod())
-                .status(archive.getStatus()) 
                 .thumbnail(archive.getThumbnail())
                 .link(archive.getLink())
                 .skills(archive.getSkillList())
                 .imgUrls(archive.getImageUrlsAsList())
                 .view(archive.getView())
                 .bookmark(archive.getBookmark())
+                .members(memberDTOs)
                 .createdDateTime(archive.getCreatedDateTime())
                 .modifiedDateTime(archive.getModifiedDateTime())
                 .build();
diff --git a/sequence_member/src/main/java/sequence/sequence_member/archive/service/TeamEvaluationService.java b/sequence_member/src/main/java/sequence/sequence_member/archive/service/TeamEvaluationService.java
new file mode 100644
index 0000000..ea2e46a
--- /dev/null
+++ b/sequence_member/src/main/java/sequence/sequence_member/archive/service/TeamEvaluationService.java
@@ -0,0 +1,197 @@
+package sequence.sequence_member.archive.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import sequence.sequence_member.archive.dto.TeamEvaluationRequestDto;
+import sequence.sequence_member.archive.entity.ArchiveMember;
+import sequence.sequence_member.archive.entity.TeamEvaluation;
+import sequence.sequence_member.archive.repository.TeamEvaluationRepository;
+import sequence.sequence_member.global.enums.enums.Status;
+import sequence.sequence_member.global.exception.BAD_REQUEST_EXCEPTION;
+import sequence.sequence_member.archive.repository.ArchiveMemberRepository;
+import sequence.sequence_member.archive.entity.Archive;
+import sequence.sequence_member.archive.repository.ArchiveRepository;
+import sequence.sequence_member.member.entity.MemberEntity;
+import sequence.sequence_member.member.repository.MemberRepository;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import sequence.sequence_member.global.exception.ArchiveNotFoundException;
+import java.util.stream.Collectors;
+import sequence.sequence_member.archive.dto.TeamEvaluationResponseDto;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class TeamEvaluationService {
+
+    private final TeamEvaluationRepository teamEvaluationRepository;
+    private final ArchiveMemberRepository archiveMemberRepository;
+    private final ArchiveRepository archiveRepository;
+    private final MemberRepository memberRepository;
+
+    @Transactional
+    public void createTeamEvaluation(
+            Long archiveId,
+            String username,
+            TeamEvaluationRequestDto requestDto) {
+            
+        // archive 존재 여부 먼저 확인
+        Archive archive = archiveRepository.findById(archiveId)
+            .orElseThrow(() -> new ArchiveNotFoundException("아카이브 정보를 찾을 수 없습니다."));
+            
+        // username으로 멤버 찾기
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new BAD_REQUEST_EXCEPTION("사용자를 찾을 수 없습니다."));
+            
+        // 평가자의 ArchiveMember 정보 조회
+        ArchiveMember evaluator = archiveMemberRepository.findByMemberAndArchive_Id(member, archiveId);
+        if (evaluator == null) {
+            throw new BAD_REQUEST_EXCEPTION("해당 아카이브의 멤버가 아닙니다.");
+        }
+
+        for (TeamEvaluationRequestDto.EvaluationItem evaluation : requestDto.getEvaluations()) {
+            // 피평가자 찾기
+            MemberEntity evaluatedMember = memberRepository.findByUsername(evaluation.getEvaluatedUserName())
+                .orElseThrow(() -> new BAD_REQUEST_EXCEPTION("피평가자를 찾을 수 없습니다: " + evaluation.getEvaluatedUserName()));
+            
+            ArchiveMember evaluated = archiveMemberRepository.findByMemberAndArchive_Id(evaluatedMember, archiveId);
+            if (evaluated == null) {
+                throw new BAD_REQUEST_EXCEPTION("피평가자가 해당 아카이브의 멤버가 아닙니다: " + evaluation.getEvaluatedUserName());
+            }
+
+            // 이미 평가했는지 확인
+            if (teamEvaluationRepository.existsByEvaluatorAndEvaluated(evaluator, evaluated)) {
+                throw new BAD_REQUEST_EXCEPTION("이미 평가를 완료했습니다: " + evaluation.getEvaluatedUserName());
+            }
+
+            // 키워드 리스트를 JSON 배열 형식으로 변환
+            String keywordJson = "[\"" + String.join("\",\"", evaluation.getKeyword()) + "\"]";
+
+            // 팀 평가 엔티티 생성 및 검증
+            TeamEvaluation teamEvaluation = TeamEvaluation.builder()
+                    .evaluator(evaluator)
+                    .evaluated(evaluated)
+                    .feedback(evaluation.getFeedback())
+                    .keyword(keywordJson)
+                    .build();
+
+            if (!teamEvaluation.validateSameArchive()) {
+                throw new BAD_REQUEST_EXCEPTION("같은 아카이브의 멤버만 평가할 수 있습니다: " + evaluation.getEvaluatedUserName());
+            }
+
+            if (!teamEvaluation.validateSelfEvaluation()) {
+                throw new BAD_REQUEST_EXCEPTION("자기 자신은 평가할 수 없습니다: " + evaluation.getEvaluatedUserName());
+            }
+
+            teamEvaluationRepository.save(teamEvaluation);
+        }
+    }
+
+    // 아카이브의 모든 평가 완료 여부 확인 메소드 수정
+    public boolean isAllEvaluationCompleted(Long archiveId, String username) {
+        // username으로 멤버 찾기
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new BAD_REQUEST_EXCEPTION("사용자를 찾을 수 없습니다."));
+            
+        // 평가자의 ArchiveMember 정보 조회
+        ArchiveMember archiveMember = archiveMemberRepository.findByMemberAndArchive_Id(member, archiveId);
+        if (archiveMember == null) {
+            throw new BAD_REQUEST_EXCEPTION("해당 아카이브의 멤버가 아닙니다.");
+        }
+
+        Archive archive = archiveRepository.findById(archiveId)
+            .orElseThrow(() -> new BAD_REQUEST_EXCEPTION("아카이브 정보를 찾을 수 없습니다."));
+        return teamEvaluationRepository.isAllEvaluationCompletedInArchive(archive);
+    }
+
+    public Map<String, Status> getEvaluationStatus(Long archiveId, String username) {
+        // username으로 멤버 찾기
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new BAD_REQUEST_EXCEPTION("사용자를 찾을 수 없습니다."));
+            
+        // 평가자의 ArchiveMember 정보 조회
+        ArchiveMember evaluator = archiveMemberRepository.findByMemberAndArchive_Id(member, archiveId);
+        if (evaluator == null) {
+            throw new BAD_REQUEST_EXCEPTION("해당 아카이브의 멤버가 아닙니다.");
+        }
+
+        // 아카이브의 모든 멤버 조회
+        List<ArchiveMember> archiveMembers = archiveMemberRepository.findAllByArchiveId(archiveId);
+        Map<String, Status> statusMap = new HashMap<>();
+
+        // 각 팀원별로 평가 상태 확인
+        for (ArchiveMember archiveMember : archiveMembers) {
+            int totalMembersToEvaluate = archiveMembers.size() - 1; // 자신 제외
+            int evaluatedCount = 0;
+            
+            // 해당 팀원이 다른 팀원들을 평가했는지 확인
+            for (ArchiveMember targetMember : archiveMembers) {
+                if (archiveMember.equals(targetMember)) {
+                    continue;  // 자기 자신은 건너뛰기
+                }
+
+                boolean hasEvaluated = teamEvaluationRepository.existsByEvaluatorAndEvaluated(archiveMember, targetMember);
+                if (hasEvaluated) {
+                    evaluatedCount++;
+                }
+            }
+
+            // 해당 팀원이 모든 팀원을 평가했는지 확인
+            Status memberStatus = (evaluatedCount == totalMembersToEvaluate) ? Status.평가완료 : Status.평가전;
+            statusMap.put(archiveMember.getMember().getNickname(), memberStatus);
+        }
+
+        return statusMap;
+    }
+
+    public List<String> getEvaluators(Long archiveId) {
+        Archive archive = archiveRepository.findById(archiveId)
+                .orElseThrow(() -> new BAD_REQUEST_EXCEPTION("아카이브 정보를 찾을 수 없습니다."));
+
+        // 해당 아카이브에서 평가를 진행한 평가자들의 목록 조회
+        List<ArchiveMember> evaluators = teamEvaluationRepository.findDistinctEvaluatorsByArchive(archive);
+
+        // 평가자들의 사용자명 리스트로 변환하여 반환
+        return evaluators.stream()
+                .map(evaluator -> evaluator.getMember().getUsername())
+                .toList();
+    }
+
+    public List<TeamEvaluationResponseDto> getTeamEvaluations(Long archiveId, String username) {
+        // archive 존재 여부 확인
+        Archive archive = archiveRepository.findById(archiveId)
+            .orElseThrow(() -> new ArchiveNotFoundException("아카이브 정보를 찾을 수 없습니다."));
+        
+        // username으로 멤버 찾기
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new BAD_REQUEST_EXCEPTION("사용자를 찾을 수 없습니다."));
+            
+        // 평가자의 ArchiveMember 정보 조회
+        ArchiveMember evaluator = archiveMemberRepository.findByMemberAndArchive_Id(member, archiveId);
+        if (evaluator == null) {
+            throw new BAD_REQUEST_EXCEPTION("해당 아카이브의 멤버가 아닙니다.");
+        }
+
+        // 아카이브의 모든 멤버 조회
+        List<ArchiveMember> allMembers = archiveMemberRepository.findAllByArchiveId(archiveId);
+        
+        // 평가 대상 목록 생성 (자신 제외)
+        List<TeamEvaluationResponseDto.EvaluatedInfo> evaluatedList = allMembers.stream()
+            .filter(archiveMember -> !archiveMember.getId().equals(evaluator.getId()))
+            .map(evaluated -> TeamEvaluationResponseDto.EvaluatedInfo.builder()
+                .username(evaluated.getMember().getUsername())
+                .profileImg(evaluated.getMember().getProfileImg())
+                .build())
+            .collect(Collectors.toList());
+
+        return List.of(TeamEvaluationResponseDto.builder()
+            .evaluator(TeamEvaluationResponseDto.EvaluatorInfo.builder()
+                .username(evaluator.getMember().getUsername())
+                .profileImg(evaluator.getMember().getProfileImg())
+                .build())
+            .evaluated(evaluatedList)
+            .build());
+    }
+} 
diff --git a/sequence_member/src/main/java/sequence/sequence_member/global/exception/ArchiveNotFoundException.java b/sequence_member/src/main/java/sequence/sequence_member/global/exception/ArchiveNotFoundException.java
new file mode 100644
index 0000000..9817787
--- /dev/null
+++ b/sequence_member/src/main/java/sequence/sequence_member/global/exception/ArchiveNotFoundException.java
@@ -0,0 +1,7 @@
+package sequence.sequence_member.global.exception;
+
+public class ArchiveNotFoundException extends RuntimeException {
+    public ArchiveNotFoundException(String message) {
+        super(message);
+    }
+} 
\ No newline at end of file
diff --git a/sequence_member/src/main/java/sequence/sequence_member/global/exception/BAD_REQUEST_EXCEPTION.java b/sequence_member/src/main/java/sequence/sequence_member/global/exception/BAD_REQUEST_EXCEPTION.java
new file mode 100644
index 0000000..a48c8eb
--- /dev/null
+++ b/sequence_member/src/main/java/sequence/sequence_member/global/exception/BAD_REQUEST_EXCEPTION.java
@@ -0,0 +1,7 @@
+package sequence.sequence_member.global.exception;
+
+public class BAD_REQUEST_EXCEPTION extends RuntimeException {
+    public BAD_REQUEST_EXCEPTION(String message) {
+        super(message);
+    }
+}
\ No newline at end of file
diff --git a/sequence_member/src/main/java/sequence/sequence_member/global/exception/BadRequestExeption.java b/sequence_member/src/main/java/sequence/sequence_member/global/exception/BadRequestExeption.java
deleted file mode 100644
index 18cccb6..0000000
--- a/sequence_member/src/main/java/sequence/sequence_member/global/exception/BadRequestExeption.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package sequence.sequence_member.global.exception;
-
-public class BadRequestExeption extends RuntimeException {
-    public BadRequestExeption(String message) {
-        super(message);
-    }
-}
diff --git a/sequence_member/src/main/java/sequence/sequence_member/global/exception/GlobalExceptionHandler.java b/sequence_member/src/main/java/sequence/sequence_member/global/exception/GlobalExceptionHandler.java
index 22d8890..e8d1586 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/global/exception/GlobalExceptionHandler.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/global/exception/GlobalExceptionHandler.java
@@ -112,11 +112,10 @@ public ResponseEntity<ApiResponseData<String>> handleCustomNotFoundException(Aut
     }
 
     // BadRequestException 예외 처리
-    @ExceptionHandler(BadRequestExeption.class)
-    public ResponseEntity<ApiResponseData<String>> handleCustomNotFoundException(BadRequestExeption ex){
+    @ExceptionHandler(BAD_REQUEST_EXCEPTION.class)
+    public ResponseEntity<ApiResponseData<String>> handleCustomNotFoundException(BAD_REQUEST_EXCEPTION ex){
         Code code = Code.BAD_REQUEST;
         // 반환할 메시지와 HTTP 상태 코드 설정
         return ResponseEntity.status(code.getStatus()).body(ApiResponseData.of(code.getCode(), code.getMessage()+": "+ ex.getMessage(),null));
     }
-
 }
\ No newline at end of file
diff --git a/sequence_member/src/main/java/sequence/sequence_member/global/response/Code.java b/sequence_member/src/main/java/sequence/sequence_member/global/response/Code.java
index b2583b4..fbe4468 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/global/response/Code.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/global/response/Code.java
@@ -14,6 +14,8 @@ public enum Code {
      * 성공 0번대
      */
     SUCCESS(HttpStatus.OK, 200, "성공적으로 처리되었습니다."),
+    CREATED(HttpStatus.CREATED, 201, "성공적으로 생성되었습니다."),
+    ALREADY_EXISTS(HttpStatus.OK, 202, "이미 존재하는 리소스입니다."),
 
     /**
      * VALIDATION 관련 100번대
diff --git a/sequence_member/src/main/java/sequence/sequence_member/member/config/SecurityConfig.java b/sequence_member/src/main/java/sequence/sequence_member/member/config/SecurityConfig.java
index e249139..69500f7 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/member/config/SecurityConfig.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/member/config/SecurityConfig.java
@@ -98,7 +98,7 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request){
                 .authorizeHttpRequests((auth)->auth
                         .requestMatchers("/api/login", "/api/users/join", "/api/token", "/api/users/check_username", "/api/users/check_email", "/api/users/check_nickname", "/api/user/delete").permitAll()
                         .requestMatchers(HttpMethod.GET,"/api/projects/**").permitAll()
-                        .requestMatchers("/api/archive/**").permitAll()
+                        .requestMatchers("/api/archive/**").authenticated()
                         .requestMatchers("/error").permitAll() // 예외 처리 로직 없을 때 Tomcat이 포워딩 하는 경로 접근 허용하여 예외 메세지 잘 전달되도록 허용
                         .anyRequest().authenticated());
 
diff --git a/sequence_member/src/main/java/sequence/sequence_member/member/dto/LoginOutputDTO.java b/sequence_member/src/main/java/sequence/sequence_member/member/dto/LoginOutputDTO.java
new file mode 100644
index 0000000..531d75a
--- /dev/null
+++ b/sequence_member/src/main/java/sequence/sequence_member/member/dto/LoginOutputDTO.java
@@ -0,0 +1,11 @@
+package sequence.sequence_member.member.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class LoginOutputDTO {
+    String nickname;
+    String profileImg;
+}
diff --git a/sequence_member/src/main/java/sequence/sequence_member/member/jwt/LoginFilter.java b/sequence_member/src/main/java/sequence/sequence_member/member/jwt/LoginFilter.java
index 1e51ece..6965326 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/member/jwt/LoginFilter.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/member/jwt/LoginFilter.java
@@ -21,6 +21,7 @@
 import sequence.sequence_member.global.exception.BaseException;
 import sequence.sequence_member.global.response.ApiResponseData;
 import sequence.sequence_member.global.response.Code;
+import sequence.sequence_member.member.dto.LoginOutputDTO;
 import sequence.sequence_member.member.entity.MemberEntity;
 import sequence.sequence_member.member.repository.MemberRepository;
 import sequence.sequence_member.member.service.TokenReissueService;
@@ -87,16 +88,14 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR
         //유저 이름 찾기
         String username = authentication.getName();
         MemberEntity memberEntity = memberRepository.findByUsername(username).orElseThrow(()->new BaseException("해당 유저가 존재하지 않습니다."));
-        String nickname = memberEntity.getNickname();
-
+        
         String access = jwtUtil.createJwt("access",username,  600000L*60*24*100); // 24시간 *100 = 100일. 테스트를 위해 기한 늘림
         String refresh = jwtUtil.createJwt("refresh", username, 86400000L*100); // 24시간 *100 = 100일
 
         tokenReissueService.RefreshTokenSave(username,refresh,86400000L*100);
 
         // 실패 응답 객체 생성
-        ResponseEntity<ApiResponseData<String>> responseBody = ResponseEntity.ok().body(ApiResponseData.success(nickname, "로그인을 성공하였습니다."));
-
+        ResponseEntity<ApiResponseData<LoginOutputDTO>> responseBody = ResponseEntity.ok().body(ApiResponseData.success(new LoginOutputDTO(memberEntity.getNickname(), memberEntity.getProfileImg()), "로그인을 성공하였습니다."));
 
         response.setHeader("access", access);
         response.addCookie(createCookie("refresh", refresh));
diff --git a/sequence_member/src/main/java/sequence/sequence_member/member/service/InviteAccessService.java b/sequence_member/src/main/java/sequence/sequence_member/member/service/InviteAccessService.java
index ab7ae1f..24906b1 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/member/service/InviteAccessService.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/member/service/InviteAccessService.java
@@ -6,7 +6,7 @@
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import sequence.sequence_member.global.exception.BadRequestExeption;
+import sequence.sequence_member.global.exception.BAD_REQUEST_EXCEPTION;
 import sequence.sequence_member.global.exception.UserNotFindException;
 import sequence.sequence_member.member.dto.AcceptProjectOutputDTO;
 import sequence.sequence_member.member.dto.CustomUserDetails;
@@ -55,7 +55,7 @@ public List<InviteProjectOutputDTO> getInvitedProjects(CustomUserDetails customU
     @Transactional
     public void acceptInvite(CustomUserDetails customUserDetails, Long projectInvitedMemberId){
         MemberEntity member = memberRepository.findByUsername(customUserDetails.getUsername()).orElseThrow(() -> new UserNotFindException("해당 유저가 존재하지 않습니다."));
-        ProjectInvitedMember projectInvitedMember = projectInvitedMemberRepository.findByIdAndMemberId(projectInvitedMemberId,member.getId()).orElseThrow(() -> new BadRequestExeption("해당 프로젝트 초대가 유효하지 않습니다."));
+        ProjectInvitedMember projectInvitedMember = projectInvitedMemberRepository.findByIdAndMemberId(projectInvitedMemberId,member.getId()).orElseThrow(() -> new BAD_REQUEST_EXCEPTION("해당 프로젝트 초대가 유효하지 않습니다."));
 
         projectMemberRepository.save(ProjectMember.builder()
                 .member(member)
@@ -68,7 +68,7 @@ public void acceptInvite(CustomUserDetails customUserDetails, Long projectInvite
     @Transactional
     public void rejectInvite(CustomUserDetails customUserDetails, Long projectInvitedMemberId){
         MemberEntity member = memberRepository.findByUsername(customUserDetails.getUsername()).orElseThrow(() -> new UserNotFindException("해당 유저가 존재하지 않습니다."));
-        ProjectInvitedMember projectInvitedMember = projectInvitedMemberRepository.findByIdAndMemberId(projectInvitedMemberId,member.getId()).orElseThrow(() -> new BadRequestExeption("해당 프로젝트 초대가 유효하지 않습니다."));
+        ProjectInvitedMember projectInvitedMember = projectInvitedMemberRepository.findByIdAndMemberId(projectInvitedMemberId,member.getId()).orElseThrow(() -> new BAD_REQUEST_EXCEPTION("해당 프로젝트 초대가 유효하지 않습니다."));
         projectInvitedMemberRepository.delete(projectInvitedMember);
     }
 
diff --git a/sequence_member/src/main/java/sequence/sequence_member/project/dto/ProjectInputDTO.java b/sequence_member/src/main/java/sequence/sequence_member/project/dto/ProjectInputDTO.java
index 803abb6..a01bf4d 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/project/dto/ProjectInputDTO.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/project/dto/ProjectInputDTO.java
@@ -56,5 +56,4 @@ public class ProjectInputDTO {
     private String article;
 
     private String link;
-
 }
diff --git a/sequence_member/src/main/java/sequence/sequence_member/project/service/CommentService.java b/sequence_member/src/main/java/sequence/sequence_member/project/service/CommentService.java
index 91adf41..e43ed05 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/project/service/CommentService.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/project/service/CommentService.java
@@ -4,7 +4,7 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import sequence.sequence_member.global.exception.AuthException;
-import sequence.sequence_member.global.exception.BadRequestExeption;
+import sequence.sequence_member.global.exception.BAD_REQUEST_EXCEPTION;
 import sequence.sequence_member.global.exception.CanNotFindResourceException;
 import sequence.sequence_member.global.exception.UserNotFindException;
 import sequence.sequence_member.member.dto.CustomUserDetails;
@@ -61,7 +61,7 @@ public void updateComment(CustomUserDetails customUserDetails, Long projectId, L
 
         //수정하려는 댓글의 프로젝트와 요청한 프로젝트가 같은지 확인
          if(!comment.getProject().equals(project)) {
-             throw new BadRequestExeption("해당 프로젝트에 존재하지 않는 댓글입니다.");
+             throw new BAD_REQUEST_EXCEPTION("해당 프로젝트에 존재하지 않는 댓글입니다.");
          }
 
          //수정하려는 댓글의 내용을 변경
diff --git a/sequence_member/src/main/java/sequence/sequence_member/project/service/ProjectService.java b/sequence_member/src/main/java/sequence/sequence_member/project/service/ProjectService.java
index 6c89d70..4a5d3a4 100644
--- a/sequence_member/src/main/java/sequence/sequence_member/project/service/ProjectService.java
+++ b/sequence_member/src/main/java/sequence/sequence_member/project/service/ProjectService.java
@@ -11,7 +11,7 @@
 import sequence.sequence_member.global.enums.enums.Period;
 import sequence.sequence_member.global.enums.enums.Step;
 import sequence.sequence_member.global.exception.AuthException;
-import sequence.sequence_member.global.exception.BadRequestExeption;
+import sequence.sequence_member.global.exception.BAD_REQUEST_EXCEPTION;
 import sequence.sequence_member.global.exception.CanNotFindResourceException;
 import sequence.sequence_member.global.exception.UserNotFindException;
 import sequence.sequence_member.global.utils.DataConvertor;
@@ -148,7 +148,7 @@ public ProjectOutputDTO updateProject(Long projectId, CustomUserDetails customUs
         // 삭제된 멤버들은 ProjectMember에서 삭제
         List<MemberEntity> deletedMembers = memberRepository.findByNicknameIn(projectUpdateDTO.getDeletedMembersNicknames());
         if(deletedMembers.contains(writer)){
-            throw new BadRequestExeption("작성자는 멤버에서 삭제할 수 없습니다.");
+            throw new BAD_REQUEST_EXCEPTION("작성자는 멤버에서 삭제할 수 없습니다.");
         }
         projectMemberRepository.deleteByProjectAndMemberIn(project, deletedMembers);
 
diff --git a/sequence_member/src/main/resources/application.yml b/sequence_member/src/main/resources/application.yml
index beb663a..a6821e2 100644
--- a/sequence_member/src/main/resources/application.yml
+++ b/sequence_member/src/main/resources/application.yml
@@ -6,7 +6,7 @@ spring:
 
   jpa:
     hibernate:
-      ddl-auto: create
+      ddl-auto: update
     properties:
       hibernate:
         format_sql: true


Reviews:
user: login: kim946509
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-06T16:02:48Z
user: login: High-Quality-Coffee
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-07T04:37:02Z

Review comments:
diff_hunk: @@ -24,61 +28,92 @@ public class ArchiveController {
 
     // 아카이브 등록
     @PostMapping
-    public ResponseEntity<ApiResponseData<String>> createArchive(@Valid @RequestBody ArchiveRegisterInputDTO archiveRegisterInputDTO) {
-        archiveService.createArchive(archiveRegisterInputDTO);
+    public ResponseEntity<ApiResponseData<String>> createArchive(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @Valid @RequestBody ArchiveRegisterInputDTO archiveRegisterInputDTO) {
+        if (userDetails == null) {
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
+        }
path: sequence_member/src/main/java/sequence/sequence_member/archive/controller/ArchiveController.java
user: login: kim946509
body: SecurityConfig에서 권한설정을 해주거나 @Seucred 어노테이션등을  통해 로그한 유저만 접근할 수 있도록 하는 방법이 있는데, 혹시 따로 userDetails가 null인걸 검사하는 이유가 있을까요?
created_at: 2025-03-06T15:47:54Z
author_association: COLLABORATOR
diff_hunk: @@ -24,61 +28,92 @@ public class ArchiveController {
 
     // 아카이브 등록
     @PostMapping
-    public ResponseEntity<ApiResponseData<String>> createArchive(@Valid @RequestBody ArchiveRegisterInputDTO archiveRegisterInputDTO) {
-        archiveService.createArchive(archiveRegisterInputDTO);
+    public ResponseEntity<ApiResponseData<String>> createArchive(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @Valid @RequestBody ArchiveRegisterInputDTO archiveRegisterInputDTO) {
+        if (userDetails == null) {
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
+        }
+        archiveService.createArchive(archiveRegisterInputDTO, userDetails.getUsername());
         return ResponseEntity.ok(ApiResponseData.success(null, "아카이브 등록 성공"));
     }
 
     // 아카이브 등록 후 결과 조회
     @GetMapping("/{archiveId}")
-    public ResponseEntity<ApiResponseData<ArchiveOutputDTO>> getArchiveById(@PathVariable("archiveId") Long archiveId) {
-        return ResponseEntity.ok().body(ApiResponseData.of(Code.SUCCESS.getCode(), "아카이브 상세 조회 성공", archiveService.getArchiveById(archiveId)));
+    public ResponseEntity<ApiResponseData<ArchiveOutputDTO>> getArchiveById(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @PathVariable("archiveId") Long archiveId) {
+        if (userDetails == null) {
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
+        }
+        return ResponseEntity.ok().body(ApiResponseData.of(
+            Code.SUCCESS.getCode(), 
+            "아카이브 상세 조회 성공", 
+            archiveService.getArchiveById(archiveId, userDetails.getUsername())
+        ));
     }
 
     // 아카이브 수정
     @PutMapping("/{archiveId}")
-    public ResponseEntity<ApiResponseData<String>> updateArchive(@PathVariable("archiveId") Long archiveId,
-                                                                 @Valid @RequestBody ArchiveUpdateDTO archiveUpdateDTO) {
-        archiveService.updateArchive(archiveId, archiveUpdateDTO);
+    public ResponseEntity<ApiResponseData<String>> updateArchive(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @PathVariable("archiveId") Long archiveId,
+            @Valid @RequestBody ArchiveUpdateDTO archiveUpdateDTO) {
+        if (userDetails == null) {
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
+        }
+        archiveService.updateArchive(archiveId, archiveUpdateDTO, userDetails.getUsername());
         return ResponseEntity.ok(ApiResponseData.success(null, "아카이브 수정 성공"));
     }
 
-    // 아카이브 삭제
+    // 아카이브 삭제 // 
     @DeleteMapping("/{archiveId}")
-    public ResponseEntity<ApiResponseData<String>> deleteArchiveById(@PathVariable("archiveId") Long archiveId) {
-        archiveService.deleteArchive(archiveId);
+    public ResponseEntity<ApiResponseData<String>> deleteArchiveById(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @PathVariable("archiveId") Long archiveId) {
+        if (userDetails == null) {
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
+        }
+        archiveService.deleteArchive(archiveId, userDetails.getUsername());
         return ResponseEntity.ok(ApiResponseData.success(null, "아카이브 삭제 성공"));
     }
 
     // 전체 리스트 조회
     @GetMapping("/projects")
     public ResponseEntity<ApiResponseData<ArchivePageResponseDTO>> getArchiveList(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
             @RequestParam(defaultValue = "0") int page,
             @RequestParam(defaultValue = "LATEST") SortType sortType) {
         
         return ResponseEntity.ok().body(ApiResponseData.of(
                 Code.SUCCESS.getCode(),
                 "아카이브 프로젝트 리스트 조회에 성공했습니다.",
-                archiveService.getAllArchives(page, sortType)
+                archiveService.getAllArchives(page, sortType, userDetails.getUsername())
         ));
     }
 
     // 검색 (카테고리 또는 키워드)
     @GetMapping("/projects/search")
     public ResponseEntity<ApiResponseData<ArchivePageResponseDTO>> searchArchives(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
             @RequestParam(defaultValue = "0") int page,
             @RequestParam(required = false) Category category,
             @RequestParam(required = false) String keyword,
             @RequestParam(defaultValue = "LATEST") SortType sortType) {
         
+        if (userDetails == null) {
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
+        }
+
+        String username = userDetails.getUsername();
         ArchivePageResponseDTO response;
         
         if (category != null) {
-            response = archiveService.searchByCategory(category, page, sortType);
+            response = archiveService.searchByCategory(category, page, sortType, username);
         } else if (keyword != null && !keyword.trim().isEmpty()) {
-            response = archiveService.searchByTitle(keyword, page, sortType);
+            response = archiveService.searchByTitle(keyword, page, sortType, username);
         } else {
-            response = archiveService.getAllArchives(page, sortType);
+            response = archiveService.getAllArchives(page, sortType, username);
path: sequence_member/src/main/java/sequence/sequence_member/archive/controller/ArchiveController.java
user: login: kim946509
body: 동작은 크게 다르지 않다면  하나의 메서드로도 동작할 수 있게 하는건 어떨까요? 그 안에서 null검사를 통해 로직을 진행하는게 더 좋을 것 같아서요! 이렇게 하면 중복되는 코드가 많아 질 것 같습니다 :)
created_at: 2025-03-06T15:50:57Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,70 @@
+package sequence.sequence_member.archive.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import sequence.sequence_member.archive.dto.TeamEvaluationRequestDto;
+import sequence.sequence_member.archive.entity.TeamEvaluation;
+import sequence.sequence_member.archive.service.TeamEvaluationService;
+import sequence.sequence_member.global.response.ApiResponseData;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import sequence.sequence_member.member.dto.CustomUserDetails;
+import org.springframework.security.core.context.SecurityContextHolder;
+import sequence.sequence_member.global.response.Code;
+import sequence.sequence_member.global.enums.enums.Status;
+import sequence.sequence_member.archive.dto.TeamEvaluationResponseDto;
+
+import jakarta.validation.Valid;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/archive")
+public class TeamEvaluationController {
+
+    private final TeamEvaluationService teamEvaluationService;
+
+    @PostMapping("/{archiveId}/evaluations")
+    public ResponseEntity<?> createTeamEvaluation(
path: sequence_member/src/main/java/sequence/sequence_member/archive/controller/TeamEvaluationController.java
user: login: kim946509
body: ApiResponseData를 응답하도록 되어있는데 *(와일드카드)를 쓰신 이유가 있을까요?
created_at: 2025-03-06T15:51:47Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,70 @@
+package sequence.sequence_member.archive.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import sequence.sequence_member.archive.dto.TeamEvaluationRequestDto;
+import sequence.sequence_member.archive.entity.TeamEvaluation;
+import sequence.sequence_member.archive.service.TeamEvaluationService;
+import sequence.sequence_member.global.response.ApiResponseData;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import sequence.sequence_member.member.dto.CustomUserDetails;
+import org.springframework.security.core.context.SecurityContextHolder;
+import sequence.sequence_member.global.response.Code;
+import sequence.sequence_member.global.enums.enums.Status;
+import sequence.sequence_member.archive.dto.TeamEvaluationResponseDto;
+
+import jakarta.validation.Valid;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/archive")
+public class TeamEvaluationController {
+
+    private final TeamEvaluationService teamEvaluationService;
+
+    @PostMapping("/{archiveId}/evaluations")
+    public ResponseEntity<?> createTeamEvaluation(
+            @PathVariable Long archiveId,
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @Valid @RequestBody TeamEvaluationRequestDto requestDto) {
+            
+        teamEvaluationService.createTeamEvaluation(archiveId, userDetails.getUsername(), requestDto);
+        return ResponseEntity
+                .status(Code.CREATED.getStatus())
+                .body(ApiResponseData.of(Code.CREATED.getCode(), "팀원 평가가 완료되었습니다.", null));
+    }
+
+    @GetMapping("/{archiveId}/evaluations")
+    public ResponseEntity<?> getTeamEvaluations(
path: sequence_member/src/main/java/sequence/sequence_member/archive/controller/TeamEvaluationController.java
user: login: kim946509
body: 이것도 위와 동일합니다. (*)
created_at: 2025-03-06T15:52:12Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,70 @@
+package sequence.sequence_member.archive.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import sequence.sequence_member.archive.dto.TeamEvaluationRequestDto;
+import sequence.sequence_member.archive.entity.TeamEvaluation;
+import sequence.sequence_member.archive.service.TeamEvaluationService;
+import sequence.sequence_member.global.response.ApiResponseData;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import sequence.sequence_member.member.dto.CustomUserDetails;
+import org.springframework.security.core.context.SecurityContextHolder;
+import sequence.sequence_member.global.response.Code;
+import sequence.sequence_member.global.enums.enums.Status;
+import sequence.sequence_member.archive.dto.TeamEvaluationResponseDto;
+
+import jakarta.validation.Valid;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/archive")
+public class TeamEvaluationController {
+
+    private final TeamEvaluationService teamEvaluationService;
+
+    @PostMapping("/{archiveId}/evaluations")
+    public ResponseEntity<?> createTeamEvaluation(
+            @PathVariable Long archiveId,
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @Valid @RequestBody TeamEvaluationRequestDto requestDto) {
+            
+        teamEvaluationService.createTeamEvaluation(archiveId, userDetails.getUsername(), requestDto);
+        return ResponseEntity
+                .status(Code.CREATED.getStatus())
+                .body(ApiResponseData.of(Code.CREATED.getCode(), "팀원 평가가 완료되었습니다.", null));
+    }
+
+    @GetMapping("/{archiveId}/evaluations")
+    public ResponseEntity<?> getTeamEvaluations(
+            @PathVariable Long archiveId,
+            @AuthenticationPrincipal CustomUserDetails userDetails) {
+    
+        List<TeamEvaluationResponseDto> evaluations = teamEvaluationService.getTeamEvaluations(archiveId, userDetails.getUsername());
+        return ResponseEntity.ok(ApiResponseData.success(evaluations));
+    }
+
+    @GetMapping(
+        value = "/{archiveId}/evaluations/status",
+        produces = "application/json"
path: sequence_member/src/main/java/sequence/sequence_member/archive/controller/TeamEvaluationController.java
user: login: kim946509
body: 해당 메서드에 특별히 produces 속성을 설정한 이유가 있을까요?
created_at: 2025-03-06T15:53:52Z
author_association: COLLABORATOR
diff_hunk: @@ -28,23 +27,31 @@ public class ArchiveRegisterInputDTO {
     @Length(min = 1, max = 450, message = "설명은 450자 이하로 입력해주세요.")
     private String description;
 
-    @NotEmpty(message = "기간을 입력해주세요.")
-    private String duration;
+    @NotNull(message = "시작일을 입력해주세요.")
+    private LocalDate startDate;
+
+    @NotNull(message = "종료일을 입력해주세요.")
+    private LocalDate endDate;
 
     @NotNull(message = "카테고리를 선택해주세요.")
     private Category category;
 
-    @NotNull(message = "기간을 선택해주세요.")
-    private Period period;
-
-    @NotNull(message = "상태를 선택해주세요.")
-    private Status status;
 
     private String thumbnail;
     private String link;
 
     @NotEmpty(message = "관련 기술을 선택해주세요.")
     @Size(max = 20, message = "관련 기술은 20개 이하로 선택해주세요.")
     private List<String> skills;
+
+    private List<ArchiveMemberDTO> archiveMembers;
+
+    @Getter
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class ArchiveMemberDTO {
+        private String username;
+        private String role;
+    }
path: sequence_member/src/main/java/sequence/sequence_member/archive/dto/ArchiveRegisterInputDTO.java
user: login: kim946509
body: 내부 클래스로 구현하신 이유가 있을까요? 
created_at: 2025-03-06T15:54:55Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,35 @@
+package sequence.sequence_member.archive.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import sequence.sequence_member.archive.entity.Archive;
+import sequence.sequence_member.archive.entity.ArchiveMember;
+import sequence.sequence_member.archive.entity.TeamEvaluation;
+
+import java.util.List;
+
+public interface TeamEvaluationRepository extends JpaRepository<TeamEvaluation, Long> {
+    
+    // 평가자가 특정 피평가자를 평가했는지 확인
+    boolean existsByEvaluatorAndEvaluated(ArchiveMember evaluator, ArchiveMember evaluated);
+    
+    // 특정 아카이브의 모든 팀원 간 상호평가가 완료되었는지 확인
+    @Query("SELECT COUNT(te) = " +
+           "(SELECT COUNT(am1) * (COUNT(am2) - 1) FROM ArchiveMember am1, ArchiveMember am2 " +
path: sequence_member/src/main/java/sequence/sequence_member/archive/repository/TeamEvaluationRepository.java
user: login: kim946509
body: 혹시 해당 쿼리에 대해 간단하게 설명해주실수있나요? (단순 궁금) COUNT(am1 * (COUNT(am2)-1))
created_at: 2025-03-06T15:57:21Z
author_association: COLLABORATOR
diff_hunk: @@ -16,119 +16,148 @@
 import sequence.sequence_member.global.enums.enums.Category;
 import sequence.sequence_member.global.enums.enums.SortType;
 import sequence.sequence_member.global.exception.CanNotFindResourceException;
+import sequence.sequence_member.member.entity.MemberEntity;
+import sequence.sequence_member.member.repository.MemberRepository;
+import sequence.sequence_member.archive.entity.ArchiveMember;
+import sequence.sequence_member.archive.repository.ArchiveMemberRepository;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.server.ResponseStatusException;
+import sequence.sequence_member.global.enums.enums.Status;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.springframework.web.server.ResponseStatusException;
 
 @Service
 @RequiredArgsConstructor
 @Transactional(readOnly = true)
 public class ArchiveService {
     
     private final ArchiveRepository archiveRepository;
+    private final MemberRepository memberRepository;
+    private final ArchiveMemberRepository archiveMemberRepository;
 
     @Transactional
-    public ArchiveOutputDTO createArchive(ArchiveRegisterInputDTO archiveRegisterInputDTO) {
+    public ArchiveOutputDTO createArchive(ArchiveRegisterInputDTO dto, String username) {
+        // 사용자 검증
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "사용자를 찾을 수 없습니다."));
+
         Archive archive = Archive.builder()
-                .title(archiveRegisterInputDTO.getTitle())
-                .description(archiveRegisterInputDTO.getDescription())
-                .duration(archiveRegisterInputDTO.getDuration())
-                .category(archiveRegisterInputDTO.getCategory())
-                .period(archiveRegisterInputDTO.getPeriod())
-                .status(archiveRegisterInputDTO.getStatus())
-                .thumbnail(archiveRegisterInputDTO.getThumbnail())
-                .link(archiveRegisterInputDTO.getLink())
+                .title(dto.getTitle())
+                .description(dto.getDescription())
+                .startDate(dto.getStartDate())
+                .endDate(dto.getEndDate())
+                .category(dto.getCategory())
+                .status(Status.평가전)
+                .thumbnail(dto.getThumbnail())
+                .link(dto.getLink())
                 .build();
 
-        archive.setSkillsFromList(archiveRegisterInputDTO.getSkills());
-
+        archive.setSkillsFromList(dto.getSkills());
         Archive savedArchive = archiveRepository.save(archive);
 
-        return ArchiveOutputDTO.builder()
-                .id(savedArchive.getId())
-                .title(savedArchive.getTitle())
-                .description(savedArchive.getDescription())
-                .duration(savedArchive.getDuration())
-                .category(savedArchive.getCategory())
-                .period(savedArchive.getPeriod())
-                .status(savedArchive.getStatus())
-                .thumbnail(savedArchive.getThumbnail())
-                .link(savedArchive.getLink())
-                .skills(savedArchive.getSkillList())
-                .view(savedArchive.getView())
-                .bookmark(savedArchive.getBookmark())
-                .createdDateTime(savedArchive.getCreatedDateTime())
-                .modifiedDateTime(savedArchive.getModifiedDateTime())
+        // 아카이브 멤버 등록
+        for (ArchiveRegisterInputDTO.ArchiveMemberDTO memberDto : dto.getArchiveMembers()) {
+            MemberEntity archiveMember = memberRepository.findByUsername(memberDto.getUsername())
+                .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "존재하지 않는 사용자입니다: " + memberDto.getUsername()));
+
+            ArchiveMember newArchiveMember = ArchiveMember.builder()
+                .archive(savedArchive)
+                .member(archiveMember)
+                .role(memberDto.getRole())
                 .build();
+
+            archiveMemberRepository.save(newArchiveMember);
+        }
+
+        return convertToDTO(savedArchive);
     }
 
-    // 아카이브 등록 후 결과 조회
-    public ArchiveOutputDTO getArchiveById(Long archiveId) {
+    public ArchiveOutputDTO getArchiveById(Long archiveId, String username) {
+        // 사용자 검증
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "사용자를 찾을 수 없습니다."));
+
         Archive archive = archiveRepository.findById(archiveId)
-                .orElseThrow(() -> new IllegalArgumentException("해당 아카이브가 없습니다."));
-        return ArchiveOutputDTO.builder()
-                .id(archive.getId())
-                .title(archive.getTitle())
-                .description(archive.getDescription())
-                .duration(archive.getDuration())
-                .category(archive.getCategory())
-                .period(archive.getPeriod())
-                .status(archive.getStatus())
-                .thumbnail(archive.getThumbnail())
-                .link(archive.getLink())
-                .skills(archive.getSkillList())
-                .view(archive.getView())
-                .bookmark(archive.getBookmark())
-                .createdDateTime(archive.getCreatedDateTime())
-                .modifiedDateTime(archive.getModifiedDateTime())
-                .build();
+                .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "해당 아카이브가 없습니다."));
+        
+        return convertToDTO(archive);
     }
 
-    // 아카이브 내용 수정
     @Transactional
-    public void updateArchive(Long archiveId, ArchiveUpdateDTO archiveUpdateDTO) {
+    public void updateArchive(Long archiveId, ArchiveUpdateDTO archiveUpdateDTO, String username) {
+        // 사용자 검증
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "사용자를 찾을 수 없습니다."));
+
+        // 아카이브 멤버 검증
+        ArchiveMember archiveMember = archiveMemberRepository.findByMemberAndArchive_Id(member, archiveId);
+        if (archiveMember == null) {
+            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "해당 아카이브의 멤버가 아닙니다.");
+        }
+
         Archive archive = archiveRepository.findById(archiveId)
-                .orElseThrow(()-> new IllegalArgumentException("해당 아카이브가 없습니다."));
+                .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "해당 아카이브가 없습니다."));
         archive.updateArchive(archiveUpdateDTO);
     }
 
-    // 아카이브 삭제
     @Transactional
-    public void deleteArchive(Long archiveId) {
+    public void deleteArchive(Long archiveId, String username) {
+        // 사용자 검증
+        MemberEntity member = memberRepository.findByUsername(username)
+            .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "사용자를 찾을 수 없습니다."));
+
+        // 아카이브 멤버 검증
+        ArchiveMember archiveMember = archiveMemberRepository.findByMemberAndArchive_Id(member, archiveId);
+        if (archiveMember == null) {
+            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "해당 아카이브의 멤버가 아닙니다.");
path: sequence_member/src/main/java/sequence/sequence_member/archive/service/ArchiveService.java
user: login: kim946509
body: 아카이브에 속한 멤버라면 누구나 아카이브를 삭제할 수 있는건가요? 
created_at: 2025-03-06T15:59:59Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,197 @@
+package sequence.sequence_member.archive.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import sequence.sequence_member.archive.dto.TeamEvaluationRequestDto;
+import sequence.sequence_member.archive.entity.ArchiveMember;
+import sequence.sequence_member.archive.entity.TeamEvaluation;
+import sequence.sequence_member.archive.repository.TeamEvaluationRepository;
+import sequence.sequence_member.global.enums.enums.Status;
+import sequence.sequence_member.global.exception.BAD_REQUEST_EXCEPTION;
+import sequence.sequence_member.archive.repository.ArchiveMemberRepository;
+import sequence.sequence_member.archive.entity.Archive;
+import sequence.sequence_member.archive.repository.ArchiveRepository;
+import sequence.sequence_member.member.entity.MemberEntity;
+import sequence.sequence_member.member.repository.MemberRepository;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import sequence.sequence_member.global.exception.ArchiveNotFoundException;
+import java.util.stream.Collectors;
+import sequence.sequence_member.archive.dto.TeamEvaluationResponseDto;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
path: sequence_member/src/main/java/sequence/sequence_member/archive/service/TeamEvaluationService.java
user: login: kim946509
body: 클래스에  읽기전용 트랜잭션을 걸어주면 장점이 무엇인가요?
created_at: 2025-03-06T16:01:48Z
author_association: COLLABORATOR
diff_hunk: @@ -24,61 +28,92 @@ public class ArchiveController {
 
     // 아카이브 등록
     @PostMapping
-    public ResponseEntity<ApiResponseData<String>> createArchive(@Valid @RequestBody ArchiveRegisterInputDTO archiveRegisterInputDTO) {
-        archiveService.createArchive(archiveRegisterInputDTO);
+    public ResponseEntity<ApiResponseData<String>> createArchive(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @Valid @RequestBody ArchiveRegisterInputDTO archiveRegisterInputDTO) {
+        if (userDetails == null) {
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
+        }
+        archiveService.createArchive(archiveRegisterInputDTO, userDetails.getUsername());
         return ResponseEntity.ok(ApiResponseData.success(null, "아카이브 등록 성공"));
     }
 
     // 아카이브 등록 후 결과 조회
     @GetMapping("/{archiveId}")
-    public ResponseEntity<ApiResponseData<ArchiveOutputDTO>> getArchiveById(@PathVariable("archiveId") Long archiveId) {
-        return ResponseEntity.ok().body(ApiResponseData.of(Code.SUCCESS.getCode(), "아카이브 상세 조회 성공", archiveService.getArchiveById(archiveId)));
+    public ResponseEntity<ApiResponseData<ArchiveOutputDTO>> getArchiveById(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @PathVariable("archiveId") Long archiveId) {
+        if (userDetails == null) {
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
+        }
+        return ResponseEntity.ok().body(ApiResponseData.of(
+            Code.SUCCESS.getCode(), 
+            "아카이브 상세 조회 성공", 
+            archiveService.getArchiveById(archiveId, userDetails.getUsername())
+        ));
     }
 
     // 아카이브 수정
     @PutMapping("/{archiveId}")
-    public ResponseEntity<ApiResponseData<String>> updateArchive(@PathVariable("archiveId") Long archiveId,
-                                                                 @Valid @RequestBody ArchiveUpdateDTO archiveUpdateDTO) {
-        archiveService.updateArchive(archiveId, archiveUpdateDTO);
+    public ResponseEntity<ApiResponseData<String>> updateArchive(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @PathVariable("archiveId") Long archiveId,
+            @Valid @RequestBody ArchiveUpdateDTO archiveUpdateDTO) {
+        if (userDetails == null) {
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
+        }
+        archiveService.updateArchive(archiveId, archiveUpdateDTO, userDetails.getUsername());
         return ResponseEntity.ok(ApiResponseData.success(null, "아카이브 수정 성공"));
     }
 
-    // 아카이브 삭제
+    // 아카이브 삭제 // 
     @DeleteMapping("/{archiveId}")
-    public ResponseEntity<ApiResponseData<String>> deleteArchiveById(@PathVariable("archiveId") Long archiveId) {
-        archiveService.deleteArchive(archiveId);
+    public ResponseEntity<ApiResponseData<String>> deleteArchiveById(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
+            @PathVariable("archiveId") Long archiveId) {
+        if (userDetails == null) {
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
+        }
+        archiveService.deleteArchive(archiveId, userDetails.getUsername());
         return ResponseEntity.ok(ApiResponseData.success(null, "아카이브 삭제 성공"));
     }
 
     // 전체 리스트 조회
     @GetMapping("/projects")
     public ResponseEntity<ApiResponseData<ArchivePageResponseDTO>> getArchiveList(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
             @RequestParam(defaultValue = "0") int page,
             @RequestParam(defaultValue = "LATEST") SortType sortType) {
         
         return ResponseEntity.ok().body(ApiResponseData.of(
                 Code.SUCCESS.getCode(),
                 "아카이브 프로젝트 리스트 조회에 성공했습니다.",
-                archiveService.getAllArchives(page, sortType)
+                archiveService.getAllArchives(page, sortType, userDetails.getUsername())
         ));
     }
 
     // 검색 (카테고리 또는 키워드)
     @GetMapping("/projects/search")
     public ResponseEntity<ApiResponseData<ArchivePageResponseDTO>> searchArchives(
+            @AuthenticationPrincipal CustomUserDetails userDetails,
             @RequestParam(defaultValue = "0") int page,
             @RequestParam(required = false) Category category,
             @RequestParam(required = false) String keyword,
             @RequestParam(defaultValue = "LATEST") SortType sortType) {
         
+        if (userDetails == null) {
+            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
+        }
+
+        String username = userDetails.getUsername();
         ArchivePageResponseDTO response;
         
         if (category != null) {
-            response = archiveService.searchByCategory(category, page, sortType);
+            response = archiveService.searchByCategory(category, page, sortType, username);
         } else if (keyword != null && !keyword.trim().isEmpty()) {
-            response = archiveService.searchByTitle(keyword, page, sortType);
+            response = archiveService.searchByTitle(keyword, page, sortType, username);
         } else {
-            response = archiveService.getAllArchives(page, sortType);
+            response = archiveService.getAllArchives(page, sortType, username);
path: sequence_member/src/main/java/sequence/sequence_member/archive/controller/ArchiveController.java
user: login: High-Quality-Coffee
body: if else if else 구조는, 나중에 조건을 추가하거나, 
디버깅을 할때 어떤 조건문에서 오류가 발생하였는지 파악하기 어려워서 
유지보수가 힘들기 때문에 

저는 간단한 조건문이라도 가독성과 유지보수를 위해 지양하는 편입니다.
created_at: 2025-03-07T04:36:59Z
author_association: COLLABORATOR

PR comments:
user: login: kim946509
created_at: 2025-03-06T16:03:15Z
body: 수고하셨습니다!!
author_association: COLLABORATOR
user: login: Jaeboong
created_at: 2025-03-07T10:47:38Z
body: 의견 반영해서 코드 정리한 후 머지합니다.
author_association: COLLABORATOR