Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4e690d3
feat: Google 소셜 로그인 기능 안정화
vkflco08 Jun 18, 2025
cda0dfc
feat : BaseException에 에러 로깅
High-Quality-Coffee Jun 20, 2025
2915bd7
feat : project service 에 에러 로깅 추가
High-Quality-Coffee Jun 20, 2025
df47cbd
feat : project controller api 요청 로깅
High-Quality-Coffee Jun 20, 2025
42494b2
feat : 댓글, 레디스 컨트롤러 로깅
High-Quality-Coffee Jun 20, 2025
5cb9345
feat : project 도메인 로깅 추가 완료
High-Quality-Coffee Jun 23, 2025
1e33d91
refactor: 아카이브 목록 DTO 매핑 방식 변경 및 최적화
vkflco08 Jun 24, 2025
d1daa0a
feat: 소셜 로그인 연동 기능 추가
vkflco08 Jun 27, 2025
d522437
아카이브 리스트 작성자 이미지 추가
Jaeboong Jun 30, 2025
60fecd5
댓글 작성자 이미지 추가
Jaeboong Jun 30, 2025
dffc340
아카 상세 조회 시 작성자 정보 분리 및 추가
Jaeboong Jun 30, 2025
bccd5ad
test
Jaeboong Jun 30, 2025
f6353b6
application.yml roll back
Jaeboong Jun 30, 2025
445455a
feat: 간단한 이메일 폼 추가
HaHeonchan Jun 30, 2025
100d01b
Merge pull request #191 from UDR-Sequence/feat/jsb/portfolio
kim946509 Jun 30, 2025
68ac608
Merge pull request #190 from UDR-Sequence/feat/logging
kim946509 Jun 30, 2025
1f5e375
Merge pull request #192 from UDR-Sequence/feat/jsb/social-login
kim946509 Jun 30, 2025
d65029d
Merge branch 'dev' into feat/jaehwan
kim946509 Jun 30, 2025
1fc92d9
Merge pull request #193 from UDR-Sequence/feat/jaehwan
kim946509 Jun 30, 2025
39b114b
Merge pull request #194 from UDR-Sequence/feat/email
kim946509 Jun 30, 2025
8cac5ba
feat : 워크플로우 환경변수 추가
kim946509 Jun 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/main-ci-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ jobs:
GOOGLE_AUTHORIZATION_URI: ${{secrets.GOOGLE_AUTHORIZATION_URI}}
GOOGLE_TOKEN_URI: ${{secrets.GOOGLE_TOKEN_URI}}
GOOGLE_USER_INFO_URI: ${{secrets.GOOGLE_USER_INFO_URI}}
NAVER_MAIL_USERNAME: ${{secrets.NAVER_MAIL_USERNAME}}
NAVER_MAIL_PASSWORD: ${{secrets.NAVER_MAIL_PASSWORD}}

steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/main-deploy-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ jobs:
GOOGLE_AUTHORIZATION_URI: ${{secrets.GOOGLE_AUTHORIZATION_URI}}
GOOGLE_TOKEN_URI: ${{secrets.GOOGLE_TOKEN_URI}}
GOOGLE_USER_INFO_URI: ${{secrets.GOOGLE_USER_INFO_URI}}
NAVER_MAIL_USERNAME: ${{secrets.NAVER_MAIL_USERNAME}}
NAVER_MAIL_PASSWORD: ${{secrets.NAVER_MAIL_PASSWORD}}

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -178,6 +180,8 @@ jobs:
-e GOOGLE_AUTHORIZATION_URI=${{secrets.GOOGLE_AUTHORIZATION_URI}} \
-e GOOGLE_TOKEN_URI=${{secrets.GOOGLE_TOKEN_URI}} \
-e GOOGLE_USER_INFO_URI=${{secrets.GOOGLE_USER_INFO_URI}} \
-e NAVER_MAIL_USERNAME=${{secrets.NAVER_MAIL_USERNAME}} \
-e NAVER_MAIL_PASSWORD=${{secrets.NAVER_MAIL_PASSWORD}} \
${{secrets.DOCKER_USERNAME}}/sequence:latest

# monitoring 네트워크 연결 추가
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test-server-prTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ jobs:
GOOGLE_AUTHORIZATION_URI: ${{secrets.GOOGLE_AUTHORIZATION_URI}}
GOOGLE_TOKEN_URI: ${{secrets.GOOGLE_TOKEN_URI}}
GOOGLE_USER_INFO_URI: ${{secrets.GOOGLE_USER_INFO_URI}}
NAVER_MAIL_USERNAME: ${{secrets.NAVER_MAIL_USERNAME}}
NAVER_MAIL_PASSWORD: ${{secrets.NAVER_MAIL_PASSWORD}}

steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/test-server-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ jobs:
GOOGLE_AUTHORIZATION_URI: ${{secrets.GOOGLE_AUTHORIZATION_URI}}
GOOGLE_TOKEN_URI: ${{secrets.GOOGLE_TOKEN_URI}}
GOOGLE_USER_INFO_URI: ${{secrets.GOOGLE_USER_INFO_URI}}
NAVER_MAIL_USERNAME: ${{secrets.NAVER_MAIL_USERNAME}}
NAVER_MAIL_PASSWORD: ${{secrets.NAVER_MAIL_PASSWORD}}

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -178,6 +180,8 @@ jobs:
-e GOOGLE_AUTHORIZATION_URI=${{secrets.GOOGLE_AUTHORIZATION_URI}} \
-e GOOGLE_TOKEN_URI=${{secrets.GOOGLE_TOKEN_URI}} \
-e GOOGLE_USER_INFO_URI=${{secrets.GOOGLE_USER_INFO_URI}} \
-e NAVER_MAIL_USERNAME=${{secrets.NAVER_MAIL_USERNAME}} \
-e NAVER_MAIL_PASSWORD=${{secrets.NAVER_MAIL_PASSWORD}} \
${{secrets.DOCKER_USERNAME}}/test-sequence:latest
# monitoring 네트워크 연결 추가
docker network connect monitoring test-sequence-spring-container
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public class ArchiveCommentOutputDTO {
@Builder
public static class CommentDTO {
private Long id;
private String writer;
private String commentWriter;
private String commentWriterProfileImg;
private String content;
private boolean isDeleted;
private LocalDateTime createdDateTime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public static class ArchiveSimpleDTO {
private Long id;
private String title;
private String writerNickname;
private String writerProfileImg; // 작성자 프로필 이미지 추가
private String thumbnail;
private int commentCount;
private int view; // 조회수 추가
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
@AllArgsConstructor
public class ArchiveOutputDTO {
private Long id;
private String writerUsername;
private String writerNickname;
private String writerProfileImg;
private String title;
private String description;
private LocalDate startDate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class ArchiveMember extends BaseTimeEntity {
@JoinColumn(name = "archive_id")
private Archive archive;

@Column(name = "profile_img")
@Column(name = "profile_img", columnDefinition = "TEXT")
private String profileImg; // 멤버의 프로필 이미지 URL

@Builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,50 @@ public interface ArchiveRepository extends JpaRepository<Archive, Long> {
// 기본 조회 - 삭제되지 않은 아카이브만
Optional<Archive> findByIdAndIsDeletedFalse(Long id);

// 작성자 정보를 함께 조회하는 최적화된 메서드 (FETCH JOIN 사용)
@Query("SELECT a FROM Archive a JOIN FETCH a.writer WHERE a.id = :id AND a.isDeleted = false")
Optional<Archive> findByIdAndIsDeletedFalseWithWriter(@Param("id") Long id);

// 전체 목록 조회 - 삭제되지 않은 것만
Page<Archive> findByIsDeletedFalse(Pageable pageable);

// 상태별 조회 - 삭제되지 않은 것만
Page<Archive> findByStatusAndIsDeletedFalse(Status status, Pageable pageable);

// 상태별 조회 - 작성자 정보 포함 (FETCH JOIN 사용)
@Query("SELECT a FROM Archive a JOIN FETCH a.writer WHERE a.status = :status AND a.isDeleted = false")
Page<Archive> findByStatusAndIsDeletedFalseWithWriter(@Param("status") Status status, Pageable pageable);

// 카테고리별 조회 - 삭제되지 않은 것만
Page<Archive> findByCategoryAndIsDeletedFalse(Category category, Pageable pageable);

// 카테고리와 상태로 조회 - 삭제되지 않은 것만
Page<Archive> findByCategoryAndStatusAndIsDeletedFalse(Category category, Status status, Pageable pageable);

// 카테고리와 상태로 조회 - 작성자 정보 포함 (FETCH JOIN 사용)
@Query("SELECT a FROM Archive a JOIN FETCH a.writer WHERE a.category = :category AND a.status = :status AND a.isDeleted = false")
Page<Archive> findByCategoryAndStatusAndIsDeletedFalseWithWriter(@Param("category") Category category, @Param("status") Status status, Pageable pageable);

// 제목으로 검색 - 삭제되지 않은 것만
Page<Archive> findByTitleContainingIgnoreCaseAndIsDeletedFalse(String title, Pageable pageable);

// 제목으로 검색하고 상태로 필터링 - 삭제되지 않은 것만
Page<Archive> findByTitleContainingIgnoreCaseAndStatusAndIsDeletedFalse(String title, Status status, Pageable pageable);

// 제목으로 검색하고 상태로 필터링 - 작성자 정보 포함 (FETCH JOIN 사용)
@Query("SELECT a FROM Archive a JOIN FETCH a.writer WHERE a.title LIKE %:title% AND a.status = :status AND a.isDeleted = false")
Page<Archive> findByTitleContainingIgnoreCaseAndStatusAndIsDeletedFalseWithWriter(@Param("title") String title, @Param("status") Status status, Pageable pageable);

// 카테고리와 제목으로 검색 - 삭제되지 않은 것만
Page<Archive> findByCategoryAndTitleContainingIgnoreCaseAndIsDeletedFalse(Category category, String title, Pageable pageable);

// 카테고리와 제목으로 검색하고 상태로 필터링 - 삭제되지 않은 것만
Page<Archive> findByCategoryAndTitleContainingIgnoreCaseAndStatusAndIsDeletedFalse(Category category, String title, Status status, Pageable pageable);

// 카테고리와 제목으로 검색하고 상태로 필터링 - 작성자 정보 포함 (FETCH JOIN 사용)
@Query("SELECT a FROM Archive a JOIN FETCH a.writer WHERE a.category = :category AND a.title LIKE %:title% AND a.status = :status AND a.isDeleted = false")
Page<Archive> findByCategoryAndTitleContainingIgnoreCaseAndStatusAndIsDeletedFalseWithWriter(@Param("category") Category category, @Param("title") String title, @Param("status") Status status, Pageable pageable);

// 멤버 ID로 아카이브 검색 - 삭제되지 않은 것만
@Query("SELECT a FROM Archive a JOIN a.archiveMembers am WHERE am.member.id = :memberId AND a.isDeleted = false")
Page<Archive> findByMemberId(@Param("memberId") Long memberId, Pageable pageable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public ArchiveListDTO getAllArchives(int page, SortType sortType, String usernam
}

Pageable pageable = createPageableWithSort(page, sortType);
Page<Archive> archivePage = archiveRepository.findByStatusAndIsDeletedFalse(Status.평가완료, pageable);
Page<Archive> archivePage = archiveRepository.findByStatusAndIsDeletedFalseWithWriter(Status.평가완료, pageable);

List<ArchiveListDTO.ArchiveSimpleDTO> archives = archivePage.getContent().stream()
.map(archive -> {
Expand All @@ -203,6 +203,7 @@ public ArchiveListDTO getAllArchives(int page, SortType sortType, String usernam
.id(archive.getId())
.title(archive.getTitle())
.writerNickname(archive.getWriter().getNickname())
.writerProfileImg(archive.getWriter().getProfileImg())
.thumbnail(archive.getThumbnail())
.commentCount(archive.getComments().size())
.view(archive.getView())
Expand Down Expand Up @@ -236,15 +237,15 @@ public ArchiveListDTO searchArchives(
Page<Archive> archivePage;

if (category != null && keyword != null && !keyword.trim().isEmpty()) {
archivePage = archiveRepository.findByCategoryAndTitleContainingIgnoreCaseAndStatusAndIsDeletedFalse(
archivePage = archiveRepository.findByCategoryAndTitleContainingIgnoreCaseAndStatusAndIsDeletedFalseWithWriter(
category, keyword.trim(), Status.평가완료, pageable);
} else if (category != null) {
archivePage = archiveRepository.findByCategoryAndStatusAndIsDeletedFalse(category, Status.평가완료, pageable);
archivePage = archiveRepository.findByCategoryAndStatusAndIsDeletedFalseWithWriter(category, Status.평가완료, pageable);
} else if (keyword != null && !keyword.trim().isEmpty()) {
archivePage = archiveRepository.findByTitleContainingIgnoreCaseAndStatusAndIsDeletedFalse(
archivePage = archiveRepository.findByTitleContainingIgnoreCaseAndStatusAndIsDeletedFalseWithWriter(
keyword.trim(), Status.평가완료, pageable);
} else {
archivePage = archiveRepository.findByStatusAndIsDeletedFalse(Status.평가완료, pageable);
archivePage = archiveRepository.findByStatusAndIsDeletedFalseWithWriter(Status.평가완료, pageable);
}

List<ArchiveListDTO.ArchiveSimpleDTO> archives = archivePage.getContent().stream()
Expand All @@ -257,6 +258,7 @@ public ArchiveListDTO searchArchives(
.id(archive.getId())
.title(archive.getTitle())
.writerNickname(archive.getWriter().getNickname())
.writerProfileImg(archive.getWriter().getProfileImg())
.thumbnail(archive.getThumbnail())
.commentCount(archive.getComments().size())
.view(archive.getView())
Expand Down Expand Up @@ -287,9 +289,17 @@ private Pageable createPageableWithSort(int page, SortType sortType) {
return PageRequest.of(page, 18, sort);
}

// 댓글 작성자의 프로필 이미지를 가져오는 헬퍼 메서드
private String getCommentWriterProfileImg(String writerNickname) {
return memberRepository.findByNickname(writerNickname)
.map(MemberEntity::getProfileImg)
.orElse("default.png"); // 기본 이미지
}

// Archive 엔티티를 DTO로 변환
private ArchiveOutputDTO convertToDTO(Archive archive, String username, int viewCount) {
List<ArchiveOutputDTO.ArchiveMemberDTO> memberDTOs = archive.getArchiveMembers().stream()
.filter(archiveMember -> !archiveMember.getMember().getId().equals(archive.getWriter().getId()))
.map(archiveMember -> ArchiveOutputDTO.ArchiveMemberDTO.builder()
.username(archiveMember.getMember().getUsername())
.nickname(archiveMember.getMember().getNickname())
Expand Down Expand Up @@ -319,7 +329,8 @@ private ArchiveOutputDTO convertToDTO(Archive archive, String username, int view
for (ArchiveComment parentComment : parentComments) {
ArchiveCommentOutputDTO.CommentDTO parentDTO = ArchiveCommentOutputDTO.CommentDTO.builder()
.id(parentComment.getId())
.writer(parentComment.getWriter())
.commentWriter(parentComment.getWriter())
.commentWriterProfileImg(getCommentWriterProfileImg(parentComment.getWriter()))
.content(parentComment.isDeleted() ? "삭제된 댓글입니다." : parentComment.getContent())
.isDeleted(parentComment.isDeleted())
.createdDateTime(parentComment.getCreatedDateTime())
Expand All @@ -333,7 +344,8 @@ private ArchiveOutputDTO convertToDTO(Archive archive, String username, int view
for (ArchiveComment childComment : childComments) {
ArchiveCommentOutputDTO.CommentDTO childDTO = ArchiveCommentOutputDTO.CommentDTO.builder()
.id(childComment.getId())
.writer(childComment.getWriter())
.commentWriter(childComment.getWriter())
.commentWriterProfileImg(getCommentWriterProfileImg(childComment.getWriter()))
.content(childComment.isDeleted() ? "삭제된 댓글입니다." : childComment.getContent())
.isDeleted(childComment.isDeleted())
.createdDateTime(childComment.getCreatedDateTime())
Expand All @@ -347,7 +359,9 @@ private ArchiveOutputDTO convertToDTO(Archive archive, String username, int view

return ArchiveOutputDTO.builder()
.id(archive.getId())
.writerUsername(archive.getWriter().getUsername())
.writerNickname(archive.getWriter().getNickname())
.writerProfileImg(archive.getWriter().getProfileImg())
.title(archive.getTitle())
.description(archive.getDescription())
.startDate(archive.getStartDate())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package sequence.sequence_member.global.exception;

import lombok.extern.slf4j.Slf4j;
import sequence.sequence_member.global.response.Code;

@Slf4j
public class AuthException extends BaseException {
public AuthException(String message) {
super(Code.ACCESS_DENIED,message);
log.error("접근 불가");
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package sequence.sequence_member.global.exception;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BAD_REQUEST_EXCEPTION extends RuntimeException {
public BAD_REQUEST_EXCEPTION(String message) {
super(message);
log.error("Bad Request error");
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package sequence.sequence_member.global.exception;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import sequence.sequence_member.global.response.Code;

import static sequence.sequence_member.global.response.Code.INTERNAL_SERVER_ERROR;

@Slf4j
@Getter
public class BaseException extends RuntimeException {

Expand All @@ -14,48 +16,56 @@ public class BaseException extends RuntimeException {
public BaseException() {
super(INTERNAL_SERVER_ERROR.getMessage());
this.errorCode = INTERNAL_SERVER_ERROR;
log.error("BaseException 발생");
}

// 에러 메시지를 받는 생성자
public BaseException(String message) {
super(message);
this.errorCode = INTERNAL_SERVER_ERROR;
log.error("BaseException 발생");
}

// 에러 메시지와 원인을 받는 생성자
public BaseException(String message, Throwable cause) {
super(message, cause);
this.errorCode = INTERNAL_SERVER_ERROR;
log.error("BaseException 발생");
}

// 원인만을 받는 생성자
public BaseException(Throwable cause) {
super(cause);
this.errorCode = INTERNAL_SERVER_ERROR;
log.error("BaseException 발생");
}

// 에러 코드를 지정하는 생성자
public BaseException(Code errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
log.error("BaseException 발생");
}

// 에러 코드와 메시지를 받는 생성자
public BaseException(Code errorCode, String message) {
super(message);
this.errorCode = errorCode;
log.error("BaseException 발생");
}

// 에러 코드, 메시지, 원인을 받는 생성자
public BaseException(Code errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
log.error("BaseException 발생");
}

// 에러 코드와 원인을 받는 생성자
public BaseException(Code errorCode, Throwable cause) {
super(cause);
this.errorCode = errorCode;
log.error("BaseException 발생");
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package sequence.sequence_member.global.exception;

import lombok.extern.slf4j.Slf4j;
import sequence.sequence_member.global.response.Code;

@Slf4j
public class CanNotFindResourceException extends BaseException {
public CanNotFindResourceException(String message) {
super(Code.CAN_NOT_FIND_RESOURCE,message);
log.error("해당 리소스 발견 불가");
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package sequence.sequence_member.global.exception;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class UserNotFindException extends RuntimeException {
public UserNotFindException(String message) {
super(message);
log.error("해당 유저를 찾을 수 없습니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,24 @@

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Slf4j
@Component
public class OAuth2FailureHandler implements AuthenticationFailureHandler {

private static final Logger logger = LoggerFactory.getLogger(OAuth2FailureHandler.class);

@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException {

logger.error("소셜 로그인 실패: {}", exception.getMessage());
exception.printStackTrace(); // 에러 로그 확인
log.error("소셜 로그인 실패: {}", exception.getMessage());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"error\": \"소셜 로그인에 실패했습니다: " + exception.getMessage() + "\"}");
Expand Down
Loading
Loading