Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 15 additions & 7 deletions .github/workflows/ci-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,18 @@ jobs:
echo "${{ secrets.APPLICATION_TEST_YML }}" > ./src/test/resources/application-test.yml


# 1-4. gradle 환경 설치
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0

# 1-4. 성능 향상을 위한 Gradle 캐싱
- name: Gradle Caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
gradle-${{ runner.os }}

# 1-5. 테스트 컨테이너들 작동여부 체크
- name: MySQL 체크
run: |
until nc -z localhost 3306; do
Expand All @@ -100,11 +108,11 @@ jobs:
sleep 3
done

# 1-5. 빌드
# 2. 빌드
- name: Build with Gradle Wrapper
run: ./gradlew clean build

## 테스트 커버리지
# 3. 테스트 커버리지
- name: Test Coverage Report
id: jacoco
uses: madrapps/jacoco-report@v1.2
Expand All @@ -116,7 +124,7 @@ jobs:
min-coverage-overall: 0
debug-mode: true

# 7. JUnit 테스트 결과 게시
# 4. JUnit 테스트 결과 게시
- name: Test 결과 출력
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.zipte.platform.server.adapter.in.web;

import com.zipte.platform.core.response.ApiResponse;
import com.zipte.platform.security.oauth2.domain.PrincipalDetails;
import com.zipte.platform.server.adapter.in.web.dto.response.*;
import com.zipte.platform.server.adapter.in.web.swagger.DashBoardApiSpec;
import com.zipte.platform.server.application.in.dashboard.DashBoardUseCase;
import com.zipte.platform.server.domain.estate.Estate;
import com.zipte.platform.server.domain.estate.EstatePrice;
import com.zipte.platform.server.domain.region.Region;
import com.zipte.platform.server.domain.region.RegionPrice;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/api/v1/dashboard")
@RequiredArgsConstructor
public class DashBoardApi implements DashBoardApiSpec {

private final DashBoardUseCase dashboardService;

@GetMapping("/region")
public ApiResponse<List<RegionResponse>> getMyRegion(@AuthenticationPrincipal PrincipalDetails principalDetails) {

/// 유저 아이디 추출하기 -> AOP로 한번에 처리하기?
Long userId = principalDetails.getId();

List<Region> region = dashboardService.getFavoriteRegion(userId);

List<RegionResponse> responses = RegionResponse.from(region);
return ApiResponse.ok(responses);
}
Comment on lines +28 to +37
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

사용자 ID 추출 로직의 중복을 개선해주세요.

모든 엔드포인트에서 동일한 사용자 ID 추출 로직이 반복되고 있습니다. AOP나 베이스 컨트롤러를 활용하여 중복을 제거하는 것을 고려해보세요.

AOP를 활용한 예시:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtractUserId {
}

@Aspect
@Component
public class UserIdExtractionAspect {
    @Around("@annotation(ExtractUserId)")
    public Object extractUserId(ProceedingJoinPoint joinPoint) throws Throwable {
        // PrincipalDetails에서 userId 추출하여 메서드 파라미터로 전달
    }
}
🤖 Prompt for AI Agents
In src/main/java/com/zipte/platform/server/adapter/in/web/DashBoardApi.java
around lines 27 to 36, the user ID extraction from PrincipalDetails is
duplicated across endpoints. Refactor by creating a custom annotation and an AOP
aspect that intercepts methods annotated with it, extracts the user ID from
PrincipalDetails, and injects it as a method parameter. Then update the
controller methods to use this annotation and accept the user ID directly,
removing the manual extraction code.



@GetMapping("/region/price")
public ApiResponse<List<RegionPriceResponse>> getMyRegionPrice(@AuthenticationPrincipal PrincipalDetails principalDetails) {

/// 유저 아이디 추출하기
Long userId = principalDetails.getId();

List<RegionPrice> regionPrices = dashboardService.getFavoriteRegionPrices(userId);

List<RegionPriceResponse> responses = RegionPriceResponse.from(regionPrices);

return ApiResponse.ok(responses);
}


@GetMapping("/estate")
public ApiResponse<List<EstateDetailResponse>> getMyEstate(@AuthenticationPrincipal PrincipalDetails principalDetails) {

/// 유저 아이디 추출하기
Long userId = principalDetails.getId();

List<Estate> estates = dashboardService.getFavoriteEstates(userId);

List<EstateDetailResponse> responses = EstateDetailResponse.from(estates);

return ApiResponse.ok(responses);
}

@GetMapping("/estate/price")
public ApiResponse<List<EstatePriceListResponse>> getMyEstatePrice(@AuthenticationPrincipal PrincipalDetails principalDetails) {

/// 유저 아이디 추출하기
Long userId = principalDetails.getId();

/// 서비스 계층 추출
List<EstatePrice> estatePrices = dashboardService.getFavoriteEstatePrice(userId);

List<EstatePriceListResponse> responses = EstatePriceListResponse.from(estatePrices);

return ApiResponse.ok(responses);
}

@GetMapping("/estate/myhome")
public ApiResponse<List<EstatePriceListResponse>> getMyHomeEstate(@AuthenticationPrincipal PrincipalDetails principalDetails) {

/// 유저 아이디 추출하기
Long userId = principalDetails.getId();

/// 서비스 계층 추출
List<EstatePrice> myEstatePrices = dashboardService.getMyEstatePrices(userId);

///
List<EstatePriceListResponse> responses = EstatePriceListResponse.from(myEstatePrices);

return ApiResponse.ok(responses);
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,4 @@ public static List<EstateDetailResponse> from(List<Estate> estates) {
.map(EstateDetailResponse::from)
.toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.zipte.platform.server.domain.region.RegionPrice;
import lombok.Builder;

import java.util.*;

@Builder
public record RegionPriceResponse(
@JsonProperty("지역코드")
Expand All @@ -20,7 +22,6 @@ public record RegionPriceResponse(
@JsonProperty("25~30평")
String between25and30,


@JsonProperty("30평 이상")
String upper30
) {
Expand All @@ -35,8 +36,28 @@ public static RegionPriceResponse from(RegionPrice regionPrice) {
.build();
}


public static List<RegionPriceResponse> from(List<RegionPrice> regionPrices) {
return regionPrices.stream()
.map(RegionPriceResponse::from)
.toList();
}

/// 내부 함수
private static String format(Double price) {
if (price == null) return "-";
return String.format("%.0f만원", price);

long value = price.longValue(); // 만원 단위 정수화
long billion = value / 10000; // 억 단위
long rest = value % 10000; // 천만원 이하

if (billion > 0 && rest > 0) {
return String.format("%d억 %,d만원", billion, rest);
} else if (billion > 0) {
return String.format("%d억", billion);
} else {
return String.format("%,d만원", rest);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.zipte.platform.server.adapter.in.web.swagger;

import com.zipte.platform.core.response.ApiResponse;
import com.zipte.platform.security.oauth2.domain.PrincipalDetails;
import com.zipte.platform.server.adapter.in.web.dto.response.EstateDetailResponse;
import com.zipte.platform.server.adapter.in.web.dto.response.EstatePriceListResponse;
import com.zipte.platform.server.adapter.in.web.dto.response.RegionPriceResponse;
import com.zipte.platform.server.adapter.in.web.dto.response.RegionResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.core.annotation.AuthenticationPrincipal;

import java.util.List;

@Tag(name = "대시보드 API", description = "대시보드 관련 API")
public interface DashBoardApiSpec {

@Operation(
summary = "나의 관심 지역 목록 조회",
description = "JWT 를 통해서 나의 관심 지역 목록을 조회합니다."
)
ApiResponse<List<RegionResponse>> getMyRegion(
@AuthenticationPrincipal PrincipalDetails principalDetails);


@Operation(
summary = "나의 관심 지역 가격 조회",
description = "JWT 를 통해서 나의 관심 지역 가격을 조회합니다. (그래프 생성 데이터 전달)"
)
ApiResponse<List<RegionPriceResponse>> getMyRegionPrice(
@AuthenticationPrincipal PrincipalDetails principalDetails);


@Operation(
summary = "나의 관심 아파트 목록 조회",
description = "JWT 를 통해서 나의 관심 아파트 목록을 조회합니다."
)
ApiResponse<List<EstateDetailResponse>> getMyEstate(
@AuthenticationPrincipal PrincipalDetails principalDetails);


@Operation(
summary = "나의 관심 아파트 가격 조회",
description = "JWT 를 통해서 나의 관심 아파트 가격을 조회합니다. (그래프 생성 데이터 전달)"
)
ApiResponse<List<EstatePriceListResponse>> getMyEstatePrice(
@AuthenticationPrincipal PrincipalDetails principalDetails);



@Operation(
summary = "나의 아파트 조회",
description = "JWT 를 통해서 나의 거주 인증 아파트를 조회합니다. (그래프 생성 데이터 전달)"
)
ApiResponse<List<EstatePriceListResponse>> getMyHomeEstate(
@AuthenticationPrincipal PrincipalDetails principalDetails);


}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
@RequiredArgsConstructor
public class EstateOwnerShipAdapter implements EstateOwnerShipPort {
Expand All @@ -33,4 +35,12 @@ public void deleteOwnership(Long userId, String kaptCode) {
repository.deleteByKaptCodeAndUserId(kaptCode, userId);
}

@Override
public List<EstateOwnership> loadMyOwnerships(Long userId) {

return repository.findByUserId(userId).stream()
.map(EstateOwnershipJpaEntity::toDomain)
.toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,11 @@ public List<Estate> loadEstatesByRegion(String region) {
.map(EstateDocument::toDomain)
.toList();
}

@Override
public List<Estate> loadEstatesByCodes(List<String> kaptCodes) {
return repository.findByKaptCodeIn(kaptCodes).stream()
.map(EstateDocument::toDomain)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,11 @@ public List<EstatePrice> loadEstatePriceByCodeAndArea(String kaptCode, double ex
public List<EstatePrice> loadAllEstatePricesBetween(String kaptCode, String from, String to) {
return List.of();
}

@Override
public List<EstatePrice> loadEstatePricesByCodes(List<String> kaptCodes) {
return repository.findByKaptCodeIn(kaptCodes).stream()
.map(EstatePriceDocument::toDomain)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ public Page<Favorite> loadUserFavoriteByType(Long userId, FavoriteType type, Pag
.map(FavoriteJpaEntity::toDomain);
}

@Override
public List<Favorite> loadUserFavoriteByType(Long userId, FavoriteType type) {
return repository.findByUserIdAndType(userId, type).stream()
.map(FavoriteJpaEntity::toDomain)
.toList();
}

@Override
public boolean checkFavoriteByUserIdAndTypeAndCode(Long userId, FavoriteType type, String code) {
if (type.equals(FavoriteType.REGION)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ public List<Region> loadChildRegionsByPrefix(String prefix, String suffix) {
.toList();
}


@Override
public List<Region> loadRegionsByCodes(List<String> codes) {
return repository.findByCodeIn(codes).stream()
.map(RegionJpaEntity::toDomain)
.toList();
}

@Override
public boolean checkExistCode(String regionCode) {
return repository.existsByCode(regionCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;

Expand Down Expand Up @@ -50,6 +51,13 @@ public boolean checkRegionPriceExist(String regionCode) {
return repository.existsByRegionCode(regionCode);
}

@Override
public List<RegionPrice> loadRegionPriceByCodes(List<String> regionCodes) {
return repository.findByRegionCodeIn(regionCodes).stream()
.map(RegionPriceJpaEntity::toDomain)
.toList();
}

@Override
public void deleteRegionPriceByCode(String regionCode) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface EstateOwnershipJpaRepository extends JpaRepository<EstateOwnershipJpaEntity, Long> {

boolean existsByKaptCodeAndUserId(String kaptCode, Long userId);

void deleteByKaptCodeAndUserId(String kaptCode, Long userId);

List<EstateOwnershipJpaEntity> findByUserId(Long userId);


}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public interface FavoriteJpaRepository extends JpaRepository<FavoriteJpaEntity,

Page<FavoriteJpaEntity> findByUserIdAndType(Long userId, FavoriteType type, Pageable pageable);

List<FavoriteJpaEntity> findByUserIdAndType(Long userId, FavoriteType type);

Optional<FavoriteJpaEntity> findByIdAndUserId(Long id, Long userId);

boolean existsByUserIdAndKaptCode(Long userId, String kaptCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ public interface RegionJpaRepository extends JpaRepository<RegionJpaEntity, Stri

boolean existsByCode(String code);

List<RegionJpaEntity> findByCodeIn(List<String> codes);

}
Loading
Loading