Skip to content

[FEAT] Institution(요양 기관) CRUD API 구현 #11

@Uechann

Description

@Uechann

[FEAT] Institution(요양 기관) CRUD API 구현

📋 이슈 개요

요양 기관 관리를 위한 CRUD API를 구현합니다. Institution 엔티티와 관련된 InstitutionAdmin(기관 관리자), CareGiver(요양보호사), 전문 질환 정보 등을 함께 관리할 수 있는 RESTful API를 제공합니다.

🎯 목적

  • 기관 정보 관리: 요양원, 주간보호센터 등 기관의 기본 정보 관리
  • 기관 관리자 관리: 기관을 운영하는 관리자 계정 관리
  • 기관 검색 및 필터링: 위치, 기관 유형, 승인 상태별 기관 검색
  • 기관 승인 프로세스: 기관 등록 후 승인 대기 → 승인/거부 워크플로우
  • 확장 가능성: 향후 인증 기능과 통합 가능한 구조 설계

🔍 현재 상태

도메인 엔티티 구조

Institution (요양 기관)
├── id: Long
├── name: String
├── owner: InstitutionAdmin (1:1, OWNER 역할)
├── institutionType: InstitutionType (NURSING_HOME, DAY_CARE_CENTER, etc.)
├── phoneNumber: String
├── address: Address (Embedded)
├── location: GeoPoint (Embedded)
├── approvalStatus: ApprovalStatus (PENDING, APPROVED, REJECTED)
├── bedCount: Integer
├── isAdmissionAvailable: Boolean
├── specializedConditions: List<InstitutionSpecializedCondition> (1:N)
├── priceInfo: PriceInfo (Embedded)
└── openingHours: String

InstitutionAdmin (기관 관리자)
├── id: Long
├── institution: Institution (N:1)
├── email: String (unique)
├── passwordHash: String
└── role: InstitutionAdminRole (OWNER, ADMIN, STAFF)

CareGiver (요양보호사)
├── id: Long
├── institution: Institution (N:1)
├── name: String
├── phoneNumber: String
├── certificationNumber: String
└── isActive: Boolean

InstitutionSpecializedCondition (전문 질환)
├── id: Long
├── institution: Institution (N:1)
└── specializedCondition: SpecializedCondition

현재 구현된 것

  • ✅ Institution 엔티티 설계 완료
  • ✅ InstitutionAdmin 엔티티 설계 완료
  • ✅ CareGiver 엔티티 설계 완료
  • ✅ 연관관계 매핑 완료
  • ✅ ApprovalStatus, InstitutionType 등 Enum 정의 완료

구현 필요한 것

  • ❌ Repository 계층
  • ❌ Service 계층
  • ❌ Controller 계층 (API 엔드포인트)
  • ❌ DTO (Request/Response)
  • ❌ 검색 및 필터링 로직
  • ❌ 승인 프로세스 로직
  • ❌ 예외 처리

✅ 구현 목표

API 엔드포인트

1. Institution CRUD

Method Endpoint Description 인증 필요
POST /api/v1/institutions 기관 등록 신청 (PENDING 상태로 생성) ⚠️ 추후
GET /api/v1/institutions 기관 목록 조회 (검색, 필터링, 페이징)
GET /api/v1/institutions/{institutionId} 기관 상세 조회
PUT /api/v1/institutions/{institutionId} 기관 정보 수정 ⚠️ 추후
DELETE /api/v1/institutions/{institutionId} 기관 삭제 ⚠️ 추후
PATCH /api/v1/institutions/{institutionId}/approval 기관 승인 처리 (관리자) ⚠️ 추후
PATCH /api/v1/institutions/{institutionId}/admission-status 입소 가능 여부 변경 ⚠️ 추후

2. Institution 검색 및 필터링

Method Endpoint Description 인증 필요
GET /api/v1/institutions/search 위치 기반 검색 (반경 내 기관)
GET /api/v1/institutions/filter 복합 필터링 (유형, 승인상태, 입소가능 등)

검색 파라미터 예시:

  • latitude, longitude, radius - 위치 기반 검색
  • institutionType - 기관 유형
  • approvalStatus - 승인 상태
  • isAdmissionAvailable - 입소 가능 여부
  • keyword - 기관명 검색

3. InstitutionAdmin CRUD (기관 관리자)

Method Endpoint Description 인증 필요
POST /api/v1/institutions/{institutionId}/admins 기관 관리자 추가 ⚠️ 추후
GET /api/v1/institutions/{institutionId}/admins 기관 관리자 목록 ⚠️ 추후
PUT /api/v1/institutions/{institutionId}/admins/{adminId} 관리자 권한 수정 ⚠️ 추후
DELETE /api/v1/institutions/{institutionId}/admins/{adminId} 관리자 삭제 ⚠️ 추후

4. CareGiver CRUD (요양보호사)

Method Endpoint Description 인증 필요
POST /api/v1/institutions/{institutionId}/caregivers 요양보호사 등록 ⚠️ 추후
GET /api/v1/institutions/{institutionId}/caregivers 요양보호사 목록
GET /api/v1/institutions/{institutionId}/caregivers/{caregiverId} 요양보호사 상세
PUT /api/v1/institutions/{institutionId}/caregivers/{caregiverId} 요양보호사 정보 수정 ⚠️ 추후
PATCH /api/v1/institutions/{institutionId}/caregivers/{caregiverId}/active 활동 상태 변경 ⚠️ 추후
DELETE /api/v1/institutions/{institutionId}/caregivers/{caregiverId} 요양보호사 삭제 ⚠️ 추후

⚠️ 참고: 인증 기능은 별도 이슈에서 구현 중이므로, 현재는 인증 없이 구현하되 추후 통합 가능한 구조로 설계합니다.

🛠️ 구현 계획

1단계: Repository 계층 구현

📁 domain/institution/profile/repository/InstitutionRepository.java

public interface InstitutionRepository extends JpaRepository<Institution, Long> {
    
    // 승인 상태별 조회
    Page<Institution> findByApprovalStatus(ApprovalStatus status, Pageable pageable);
    
    // 기관 유형별 조회
    Page<Institution> findByInstitutionType(InstitutionType type, Pageable pageable);
    
    // 기관명 검색
    Page<Institution> findByNameContaining(String keyword, Pageable pageable);
    
    // 입소 가능 기관 조회
    Page<Institution> findByIsAdmissionAvailableAndApprovalStatus(
        Boolean isAdmissionAvailable, ApprovalStatus status, Pageable pageable);
    
    // 위치 기반 검색 (Native Query 또는 Querydsl 활용)
    @Query("SELECT i FROM Institution i WHERE " +
           "i.approvalStatus = :status AND " +
           "FUNCTION('ST_Distance_Sphere', " +
           "POINT(i.location.longitude, i.location.latitude), " +
           "POINT(:longitude, :latitude)) <= :radius")
    List<Institution> findNearbyInstitutions(
        @Param("latitude") double latitude,
        @Param("longitude") double longitude,
        @Param("radius") double radius,
        @Param("status") ApprovalStatus status
    );
}

📁 domain/institution/profile/repository/InstitutionAdminRepository.java

public interface InstitutionAdminRepository extends JpaRepository<InstitutionAdmin, Long> {
    
    Optional<InstitutionAdmin> findByEmail(String email);
    
    boolean existsByEmail(String email);
    
    List<InstitutionAdmin> findByInstitutionId(Long institutionId);
    
    Optional<InstitutionAdmin> findByIdAndInstitutionId(Long id, Long institutionId);
    
    // OWNER 역할 관리자 조회
    Optional<InstitutionAdmin> findByInstitutionIdAndRole(Long institutionId, InstitutionAdminRole role);
}

📁 domain/institution/profile/repository/CareGiverRepository.java

public interface CareGiverRepository extends JpaRepository<CareGiver, Long> {
    
    List<CareGiver> findByInstitutionId(Long institutionId);
    
    Optional<CareGiver> findByIdAndInstitutionId(Long id, Long institutionId);
    
    // 활동 중인 요양보호사만 조회
    List<CareGiver> findByInstitutionIdAndIsActive(Long institutionId, Boolean isActive);
    
    // 자격증 번호로 조회
    Optional<CareGiver> findByCertificationNumber(String certificationNumber);
    
    boolean existsByCertificationNumber(String certificationNumber);
}

2단계: DTO 설계 및 구현

📁 api/dto/institution/request/ (요청 DTO)

InstitutionCreateRequest.java - 기관 등록 신청

{
    "name": "행복 요양원",
    "institutionType": "NURSING_HOME",
    "phoneNumber": "02-1234-5678",
    "address": {
        "zipCode": "12345",
        "mainAddress": "서울시 강남구 테헤란로 123",
        "detailAddress": "1층"
    },
    "location": {
        "latitude": 37.5665,
        "longitude": 126.9780
    },
    "bedCount": 50,
    "priceInfo": {
        "monthlyFee": 2000000,
        "admissionFee": 5000000,
        "description": "식사 및 간식 포함"
    },
    "openingHours": "월-일 00:00-24:00",
    "ownerEmail": "owner@example.com",
    "ownerPassword": "hashedPassword",
    "specializedConditions": ["DEMENTIA", "STROKE"]
}

InstitutionUpdateRequest.java - 기관 정보 수정
InstitutionSearchRequest.java - 기관 검색 (위치 기반)
InstitutionFilterRequest.java - 기관 필터링
ApprovalRequest.java - 승인/거부 요청

📁 api/dto/institution/response/ (응답 DTO)

InstitutionResponse.java - 기관 기본 정보
InstitutionDetailResponse.java - 기관 상세 정보 (관리자, 요양보호사 포함)
InstitutionListResponse.java - 기관 목록 (페이징)
InstitutionSearchResponse.java - 검색 결과 (거리 정보 포함)

📁 api/dto/admin/request/

InstitutionAdminCreateRequest.java - 관리자 추가
InstitutionAdminUpdateRequest.java - 관리자 권한 수정

📁 api/dto/admin/response/

InstitutionAdminResponse.java - 관리자 정보
InstitutionAdminListResponse.java - 관리자 목록

📁 api/dto/caregiver/request/

CareGiverCreateRequest.java - 요양보호사 등록

{
    "name": "김요양",
    "phoneNumber": "010-1234-5678",
    "certificationNumber": "CG-2024-12345",
    "isActive": true
}

CareGiverUpdateRequest.java - 요양보호사 정보 수정

📁 api/dto/caregiver/response/

CareGiverResponse.java - 요양보호사 정보
CareGiverListResponse.java - 요양보호사 목록

3단계: Service 계층 구현

📁 domain/institution/profile/service/InstitutionService.java

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class InstitutionService {
    
    private final InstitutionRepository institutionRepository;
    private final InstitutionAdminRepository adminRepository;
    
    /**
     * 기관 등록 신청 (PENDING 상태로 생성)
     * - 기관 정보와 함께 OWNER 역할의 관리자도 동시 생성
     */
    @Transactional
    public InstitutionResponse createInstitution(InstitutionCreateRequest request);
    
    /**
     * 기관 목록 조회 (페이징)
     */
    public Page<InstitutionResponse> getInstitutions(Pageable pageable);
    
    /**
     * 기관 상세 조회
     */
    public InstitutionDetailResponse getInstitution(Long institutionId);
    
    /**
     * 기관 정보 수정
     */
    @Transactional
    public InstitutionResponse updateInstitution(Long institutionId, InstitutionUpdateRequest request);
    
    /**
     * 기관 승인 처리 (관리자 전용)
     */
    @Transactional
    public InstitutionResponse approveInstitution(Long institutionId, ApprovalRequest request);
    
    /**
     * 입소 가능 여부 변경
     */
    @Transactional
    public InstitutionResponse updateAdmissionStatus(Long institutionId, Boolean isAvailable);
    
    /**
     * 기관 삭제 (soft delete)
     */
    @Transactional
    public void deleteInstitution(Long institutionId);
    
    /**
     * 위치 기반 기관 검색
     */
    public List<InstitutionSearchResponse> searchNearbyInstitutions(
        double latitude, double longitude, double radius);
    
    /**
     * 복합 필터링 검색
     */
    public Page<InstitutionResponse> filterInstitutions(
        InstitutionFilterRequest filter, Pageable pageable);
}

📁 domain/institution/profile/service/InstitutionAdminService.java

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class InstitutionAdminService {
    
    private final InstitutionAdminRepository adminRepository;
    private final InstitutionRepository institutionRepository;
    
    /**
     * 기관 관리자 추가
     */
    @Transactional
    public InstitutionAdminResponse createAdmin(Long institutionId, 
                                                 InstitutionAdminCreateRequest request);
    
    /**
     * 기관 관리자 목록 조회
     */
    public List<InstitutionAdminResponse> getAdmins(Long institutionId);
    
    /**
     * 관리자 권한 수정
     */
    @Transactional
    public InstitutionAdminResponse updateAdminRole(Long institutionId, Long adminId, 
                                                     InstitutionAdminRole newRole);
    
    /**
     * 관리자 삭제 (OWNER는 삭제 불가)
     */
    @Transactional
    public void deleteAdmin(Long institutionId, Long adminId);
    
    /**
     * 이메일 중복 체크
     */
    public boolean isEmailExists(String email);
}

📁 domain/institution/profile/service/CareGiverService.java

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CareGiverService {
    
    private final CareGiverRepository careGiverRepository;
    private final InstitutionRepository institutionRepository;
    
    /**
     * 요양보호사 등록
     */
    @Transactional
    public CareGiverResponse createCareGiver(Long institutionId, CareGiverCreateRequest request);
    
    /**
     * 기관의 요양보호사 목록 조회
     */
    public List<CareGiverResponse> getCareGivers(Long institutionId);
    
    /**
     * 활동 중인 요양보호사만 조회
     */
    public List<CareGiverResponse> getActiveCareGivers(Long institutionId);
    
    /**
     * 요양보호사 상세 조회
     */
    public CareGiverResponse getCareGiver(Long institutionId, Long caregiverId);
    
    /**
     * 요양보호사 정보 수정
     */
    @Transactional
    public CareGiverResponse updateCareGiver(Long institutionId, Long caregiverId, 
                                              CareGiverUpdateRequest request);
    
    /**
     * 활동 상태 변경
     */
    @Transactional
    public CareGiverResponse updateActiveStatus(Long institutionId, Long caregiverId, Boolean isActive);
    
    /**
     * 요양보호사 삭제
     */
    @Transactional
    public void deleteCareGiver(Long institutionId, Long caregiverId);
    
    /**
     * 자격증 번호 중복 체크
     */
    public boolean isCertificationNumberExists(String certificationNumber);
}

4단계: Controller 계층 구현

📁 api/controller/InstitutionController.java

@RestController
@RequestMapping("/api/v1/institutions")
@RequiredArgsConstructor
@Tag(name = "🏥 Institution", description = "요양 기관 관리 API")
public class InstitutionController {
    
    private final InstitutionService institutionService;
    
    @PostMapping
    @Operation(summary = "기관 등록 신청", 
               description = "새로운 요양 기관을 등록 신청합니다. (승인 대기 상태)")
    public ResponseEntity<ApiResponse<InstitutionResponse>> createInstitution(
            @Valid @RequestBody InstitutionCreateRequest request);
    
    @GetMapping
    @Operation(summary = "기관 목록 조회", description = "등록된 기관 목록을 조회합니다.")
    public ResponseEntity<ApiResponse<Page<InstitutionResponse>>> getInstitutions(
            Pageable pageable);
    
    @GetMapping("/{institutionId}")
    @Operation(summary = "기관 상세 조회", description = "기관의 상세 정보를 조회합니다.")
    public ResponseEntity<ApiResponse<InstitutionDetailResponse>> getInstitution(
            @PathVariable Long institutionId);
    
    @GetMapping("/search")
    @Operation(summary = "위치 기반 기관 검색", 
               description = "현재 위치 기반으로 반경 내 기관을 검색합니다.")
    public ResponseEntity<ApiResponse<List<InstitutionSearchResponse>>> searchNearby(
            @RequestParam double latitude,
            @RequestParam double longitude,
            @RequestParam(defaultValue = "5000") double radius);
    
    @GetMapping("/filter")
    @Operation(summary = "기관 필터링", description = "다양한 조건으로 기관을 필터링합니다.")
    public ResponseEntity<ApiResponse<Page<InstitutionResponse>>> filterInstitutions(
            @ModelAttribute InstitutionFilterRequest filter,
            Pageable pageable);
    
    // ... 나머지 엔드포인트
}

📁 api/controller/InstitutionAdminController.java

@RestController
@RequestMapping("/api/v1/institutions/{institutionId}/admins")
@RequiredArgsConstructor
@Tag(name = "👔 Institution Admin", description = "기관 관리자 관리 API")
public class InstitutionAdminController {
    
    private final InstitutionAdminService adminService;
    
    // CRUD 엔드포인트 구현
}

📁 api/controller/CareGiverController.java

@RestController
@RequestMapping("/api/v1/institutions/{institutionId}/caregivers")
@RequiredArgsConstructor
@Tag(name = "👨‍⚕️ CareGiver", description = "요양보호사 관리 API")
public class CareGiverController {
    
    private final CareGiverService careGiverService;
    
    // CRUD 엔드포인트 구현
}

5단계: 예외 처리

📁 global/exception/InstitutionException.java

public class InstitutionNotFoundException extends BusinessException {
    public InstitutionNotFoundException() {
        super(ErrorCode.INSTITUTION_NOT_FOUND);
    }
}

public class InstitutionAlreadyApprovedException extends BusinessException {
    public InstitutionAlreadyApprovedException() {
        super(ErrorCode.INSTITUTION_ALREADY_APPROVED);
    }
}

public class InstitutionAdminNotFoundException extends BusinessException {
    public InstitutionAdminNotFoundException() {
        super(ErrorCode.INSTITUTION_ADMIN_NOT_FOUND);
    }
}

public class DuplicateAdminEmailException extends BusinessException {
    public DuplicateAdminEmailException() {
        super(ErrorCode.DUPLICATE_ADMIN_EMAIL);
    }
}

public class OwnerCannotBeDeletedException extends BusinessException {
    public OwnerCannotBeDeletedException() {
        super(ErrorCode.OWNER_CANNOT_BE_DELETED);
    }
}

public class CareGiverNotFoundException extends BusinessException {
    public CareGiverNotFoundException() {
        super(ErrorCode.CAREGIVER_NOT_FOUND);
    }
}

public class DuplicateCertificationNumberException extends BusinessException {
    public DuplicateCertificationNumberException() {
        super(ErrorCode.DUPLICATE_CERTIFICATION_NUMBER);
    }
}

ErrorCode 추가

// Institution 관련
INSTITUTION_NOT_FOUND(404, "I001", "요양 기관을 찾을 수 없습니다."),
INSTITUTION_ALREADY_APPROVED(400, "I002", "이미 승인된 기관입니다."),
INSTITUTION_ACCESS_DENIED(403, "I003", "해당 기관에 접근할 수 없습니다."),

// InstitutionAdmin 관련
INSTITUTION_ADMIN_NOT_FOUND(404, "IA001", "기관 관리자를 찾을 수 없습니다."),
DUPLICATE_ADMIN_EMAIL(400, "IA002", "이미 사용 중인 관리자 이메일입니다."),
OWNER_CANNOT_BE_DELETED(400, "IA003", "기관장(OWNER)은 삭제할 수 없습니다."),

// CareGiver 관련
CAREGIVER_NOT_FOUND(404, "CG001", "요양보호사를 찾을 수 없습니다."),
DUPLICATE_CERTIFICATION_NUMBER(400, "CG002", "이미 등록된 자격증 번호입니다."),

6단계: Entity 도메인 로직 추가

📁 domain/institution/profile/entity/Institution.java (기존 파일 수정)

public class Institution extends BaseEntity {
    // ...existing fields...
    
    /**
     * 기관 정보 수정
     */
    public void updateInfo(String name, InstitutionType type, String phoneNumber,
                          Address address, GeoPoint location, Integer bedCount,
                          PriceInfo priceInfo, String openingHours) {
        this.name = name;
        this.institutionType = type;
        this.phoneNumber = phoneNumber;
        this.address = address;
        this.location = location;
        this.bedCount = bedCount;
        this.priceInfo = priceInfo;
        this.openingHours = openingHours;
    }
    
    /**
     * 승인 처리
     */
    public void approve() {
        if (this.approvalStatus == ApprovalStatus.APPROVED) {
            throw new InstitutionAlreadyApprovedException();
        }
        this.approvalStatus = ApprovalStatus.APPROVED;
    }
    
    /**
     * 승인 거부
     */
    public void reject() {
        this.approvalStatus = ApprovalStatus.REJECTED;
    }
    
    /**
     * 입소 가능 여부 변경
     */
    public void updateAdmissionAvailability(Boolean isAvailable) {
        this.isAdmissionAvailable = isAvailable;
    }
    
    /**
     * 기관장 설정
     */
    public void setOwner(InstitutionAdmin owner) {
        if (owner.getRole() != InstitutionAdminRole.OWNER) {
            throw new IllegalArgumentException("OWNER 역할이 아닙니다.");
        }
        this.owner = owner;
    }
    
    /**
     * 승인 완료 여부 확인
     */
    public boolean isApproved() {
        return this.approvalStatus == ApprovalStatus.APPROVED;
    }
}

📁 domain/institution/profile/entity/InstitutionAdmin.java (기존 파일 수정)

public class InstitutionAdmin extends BaseEntity {
    // ...existing fields...
    
    /**
     * 역할 변경
     */
    public void updateRole(InstitutionAdminRole newRole) {
        if (this.role == InstitutionAdminRole.OWNER) {
            throw new OwnerCannotBeDeletedException();
        }
        this.role = newRole;
    }
    
    /**
     * OWNER 여부 확인
     */
    public boolean isOwner() {
        return this.role == InstitutionAdminRole.OWNER;
    }
}

📁 CareGiver Entity (위치 확인 필요)

public class CareGiver extends BaseEntity {
    // ...existing fields...
    
    /**
     * 정보 수정
     */
    public void updateInfo(String name, String phoneNumber, String certificationNumber) {
        this.name = name;
        this.phoneNumber = phoneNumber;
        this.certificationNumber = certificationNumber;
    }
    
    /**
     * 활동 상태 변경
     */
    public void updateActiveStatus(Boolean isActive) {
        this.isActive = isActive;
    }
}

7단계: 검색 및 필터링 고도화

Querydsl 또는 Specification 활용

복잡한 검색 조건을 처리하기 위해 Querydsl 도입 검토:

  • 위치 기반 검색 (ST_Distance_Sphere)
  • 동적 필터링 (기관 유형, 승인 상태, 입소 가능 여부 등)
  • 정렬 (거리순, 평점순, 가격순 등)

🧪 테스트 계획

단위 테스트

  • Repository 테스트 (특히 위치 기반 쿼리)
  • Service 테스트 (승인 프로세스, 비즈니스 로직)
  • DTO 변환 테스트

통합 테스트

  • API 엔드포인트 테스트
  • 기관 등록 → 승인 워크플로우 테스트
  • 검색 및 필터링 테스트
  • 트랜잭션 테스트

📝 주의사항

1. 인증 기능 연동 대비

  • 기관 등록은 현재 누구나 가능하지만, 추후 인증된 사용자만 가능하도록 변경
  • 기관 정보 수정은 해당 기관의 관리자만 가능하도록 권한 체크 로직 추가 예정
  • 승인 처리는 시스템 관리자만 가능하도록 제한 예정

2. 승인 프로세스

  • 기관 등록 시 자동으로 PENDING 상태로 생성
  • 관리자가 수동으로 APPROVED 또는 REJECTED 처리
  • 승인된 기관만 일반 사용자에게 노출

3. 기관장(OWNER) 관리

  • 기관 등록 시 자동으로 OWNER 역할의 관리자 생성
  • OWNER는 삭제 불가
  • 기관당 OWNER는 1명만 존재

4. 위치 기반 검색

  • MySQL의 Spatial Index 활용 고려
  • 성능 최적화를 위한 인덱스 설정 필요
  • 거리 계산은 Haversine 공식 또는 ST_Distance_Sphere 함수 사용

5. 데이터 검증

  • 자격증 번호 중복 체크
  • 이메일 중복 체크
  • 위도/경도 유효성 검증
  • 가격 정보 유효성 검증

📚 참고사항

연관된 이슈

  • Member CRUD 구현 이슈 (동시 진행)
  • 인증 기능 구현 이슈 (진행 중)
  • 리뷰 기능 구현 이슈 (추후 연동)

우선순위

  1. Institution Repository 및 기본 Service 구현
  2. DTO 설계 및 구현
  3. Controller 및 기본 CRUD API 구현
  4. InstitutionAdmin 및 CareGiver CRUD 구현
  5. 검색 및 필터링 기능 구현
  6. 승인 프로세스 구현
  7. 예외 처리 및 검증
  8. 테스트 코드 작성

기술 스택 고려사항

  • Querydsl: 복잡한 동적 쿼리 처리
  • Spatial Index: 위치 기반 검색 성능 최적화
  • Redis: 자주 조회되는 기관 정보 캐싱 (추후 고려)

✅ 완료 조건

  • Institution Repository 구현
  • InstitutionAdmin Repository 구현
  • CareGiver Repository 구현
  • Service 계층 비즈니스 로직 구현
  • Controller 및 모든 API 엔드포인트 구현
  • Request/Response DTO 구현
  • 검색 및 필터링 기능 구현
  • 승인 프로세스 구현
  • 예외 처리 및 ErrorCode 정의
  • Entity 도메인 로직 추가
  • API 문서화 (Swagger)
  • 단위 테스트 작성
  • 통합 테스트 작성
  • Postman/REST Client 테스트 완료

작성일: 2025-10-24
담당자: @Uechann
예상 소요 시간: 6-7일

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions