|
1 | | -BE Readme |
| 1 | +<img src="https://capsule-render.vercel.app/api?type=waving&color=BDBDC8&height=150§ion=header" /> |
| 2 | + |
| 3 | +## 📕 문제 상황 & 해결 과정 |
| 4 | + |
| 5 | +<!-- 모집글 리스트 응답 방식 개선 --> |
| 6 | +<details> |
| 7 | +<summary>🔹 모집글 리스트 응답 방식 개선</summary> |
| 8 | + |
| 9 | +- **문제 상황** |
| 10 | + - 전체 모집글 목록을 불러오는 API에서 모든 데이터를 `List<ProjectSimpleResponse>` 형식으로 한 번에 반환하고 있었음. |
| 11 | + - 데이터가 많아질수록 응답 시간이 길어지고 메모리 사용량 증가로 인한 성능 저하 가능성이 있었음. |
| 12 | + |
| 13 | +- **해결 방안** |
| 14 | + - 대용량 처리를 위해 페이지 번호(`page`)를 입력받아, 해당 `offset`에 맞춰 필요한 개수만큼 잘라 `List`에 담아 반환하도록 구현. |
| 15 | + - 불필요한 전체 데이터 반환을 피하고, 사용자가 요청한 범위의 데이터만 선별적으로 전달 → **응답 속도 및 리소스 사용 최적화** |
| 16 | + |
| 17 | +</details> |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +<!-- 검색/정렬/필터링을 위한 동적 쿼리 구성 방법 선택 --> |
| 22 | +<details> |
| 23 | +<summary>🔹 검색/정렬/필터링을 위한 동적 쿼리 구성 방법 선택</summary> |
| 24 | + |
| 25 | +- **문제 상황** |
| 26 | + - 필터링 / 검색 / 정렬 기능을 지원해야 함 → 이를 위해 **동적 쿼리 조건 구성** 필요. |
| 27 | + |
| 28 | +- **고려한 방법** |
| 29 | + - `Specification` |
| 30 | + - `QueryDSL` |
| 31 | + |
| 32 | +- **선택 기준** |
| 33 | + - 조건의 복잡성 |
| 34 | + - 유지보수 용이성 |
| 35 | + - 코드의 안정성 (타입 안정성 등) |
| 36 | + |
| 37 | +- **선택** |
| 38 | + - 가독성, 타입 안정성, 유지보수성 측면에서 뛰어난 **QueryDSL**을 선택. |
| 39 | + |
| 40 | +- **적용** |
| 41 | + - `QueryDSL`을 사용하여 Custom Repository 구현. |
| 42 | + - 동적 쿼리 조건을 구성해 필터링 / 검색 / 정렬 기능을 유연하게 처리. |
| 43 | + |
| 44 | +</details> |
| 45 | + |
| 46 | +--- |
| 47 | + |
| 48 | +<!-- Page 응답으로 프론트 페이지네이션 정보 제공 --> |
| 49 | +<details> |
| 50 | +<summary>🔹 Page 응답으로 프론트 페이지네이션 정보 제공</summary> |
| 51 | + |
| 52 | +- **문제 상황** |
| 53 | + - 백엔드에서 페이지네이션을 적용했지만, 프론트엔드에서 **전체 페이지 수**와 **전체 데이터 수** 등의 정보를 확인할 수 없었음. |
| 54 | + |
| 55 | +- **해결 방안** |
| 56 | + - 기존에는 `List<ProjectSimpleResponse>`만 응답했지만, `Page<ProjectSimpleResponse>`를 그대로 리턴하도록 변경. |
| 57 | + - Spring Data JPA의 `Page` 객체는 `totalPages`, `totalElements`, `number`, `size` 등의 메타데이터를 자동으로 포함. |
| 58 | + |
| 59 | +- **결과** |
| 60 | + - 프론트는 응답 메타데이터를 활용해 **전체 페이지 수**, **현재 페이지 번호**, **다음 페이지 존재 여부** 등을 즉시 확인 가능. |
| 61 | + - 페이지네이션 UI 완전 구현 가능. |
| 62 | + |
| 63 | +</details> |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +<!-- 지원서 작성 시점 프로필 정보 고정(스냅샷) 필요성 --> |
| 68 | +<details> |
| 69 | +<summary>🔹 지원서 작성 시점 프로필 정보 고정(스냅샷) 필요성</summary> |
| 70 | + |
| 71 | +- **문제 상황** |
| 72 | + - `Application(지원서)` 객체는 작성 당시 사용자의 정보를 참조하기 위해 `Profile` 객체를 포함하고 있음. |
| 73 | + - 하지만 사용자가 지원서 작성 후 `Profile`을 수정하면, **이미 제출된 지원서의 정보까지 변경되는 문제** 발생. |
| 74 | + - 즉, **지원 당시의 프로필 상태**가 보존되지 않고 항상 최신 상태로 보여지는 문제가 있었음. |
| 75 | + |
| 76 | +- **원인 분석** |
| 77 | + - `Application` 객체가 JPA 연관관계를 통해 `Profile`을 직접 참조. |
| 78 | + - `@Transactional` 범위 내에서 지연 로딩된 `Profile` 변경 → 연결된 모든 곳에서 해당 변경 내용 반영. |
| 79 | + - 지원서 제출 이후 지원자가 자신의 프로필을 변경하면, **이미 제출된 지원서 내용도 변경됨**. |
| 80 | + |
| 81 | +- **해결 방안: Profile Snapshot 저장** |
| 82 | + - `Profile` 객체 전체를 **JSON 직렬화**하여 문자열 형태로 저장하도록 변경. |
| 83 | + - `Application` 도메인에 다음 메서드 추가: |
| 84 | + - `private String serializeProfile(Profile profile)` |
| 85 | + - `private Profile getProfileFromSnapshot()` |
| 86 | + - 직렬화/역직렬화에 **Jackson ObjectMapper** 사용: |
| 87 | + |
| 88 | + ```java |
| 89 | + objectMapper.writeValueAsString(profile); // 직렬화 |
| 90 | + objectMapper.readValue(snapshot, Profile.class); // 역직렬화 |
| 91 | + ``` |
| 92 | + |
| 93 | +<details> |
| 94 | +<summary>추가 문제 발생 : 직렬화 시 연관된 필드 issue</summary> |
| 95 | + |
| 96 | +- **문제 상황: @ElementCollection 연관 필드** |
| 97 | + - `Profile`에는 다음과 같은 연관된 컬렉션 필드 존재: |
| 98 | + - `techStacks` (예: 사용 기술 목록) |
| 99 | + - `workExperiences` (예: 경력 사항 등) |
| 100 | + - 이들은 `@ElementCollection`으로 별도 테이블에 저장됨 → **Lazy Loading**으로 인해 직렬화 시 자동 포함되지 않을 수 있음. |
| 101 | + - `ObjectMapper.writeValueAsString(profile)` 호출 시, JPA가 컬렉션을 아직 로딩하지 않았다면 `[]`로 직렬화됨. |
| 102 | + |
| 103 | +- **해결 방안: 직렬화 전 강제 로딩** |
| 104 | + - 직렬화 전에 명시적으로 컬렉션 필드를 강제로 로딩해야 함: |
| 105 | + |
| 106 | + ```java |
| 107 | + // 직렬화 이전 |
| 108 | + profile.getTechStacks().size(); |
| 109 | + profile.getWorkExperiences().size(); |
| 110 | + |
| 111 | + ObjectMapper.writeValueAsString(profile); // profile 객체 직렬화 |
| 112 | + ``` |
| 113 | + |
| 114 | + - 이렇게 하면 JPA가 컬렉션을 로딩한 상태로, **정확한 스냅샷 저장 가능**. |
| 115 | + |
| 116 | +</details> |
| 117 | +</details> |
| 118 | + |
| 119 | +--- |
| 120 | + |
| 121 | +관심글 기능 구현 |
| 122 | + |
| 123 | +--- |
| 124 | + |
| 125 | +포트폴리오 업로드 기능: S3 권한 문제 해결 과정 |
| 126 | + |
| 127 | +--- |
| 128 | + |
| 129 | +계정 로그인 문제 |
| 130 | + |
| 131 | +--- |
| 132 | + |
| 133 | +--- |
| 134 | + |
| 135 | +<!-- 템플릿 --> |
| 136 | +<!-- |
| 137 | +<details> |
| 138 | +<summary>🔹 템플릿</summary> |
| 139 | + |
| 140 | +<details> |
| 141 | +<summary>템플릿</summary> |
| 142 | +</details> |
| 143 | + |
| 144 | +</details> |
| 145 | +--> |
| 146 | + |
| 147 | +<img src="https://capsule-render.vercel.app/api?type=waving&color=BDBDC8&height=150§ion=footer" /> |
0 commit comments