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
50 changes: 0 additions & 50 deletions src/main/java/org/example/tackit/config/CommonDataInitializer.java

This file was deleted.

124 changes: 124 additions & 0 deletions src/main/java/org/example/tackit/config/DataInitializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package org.example.tackit.config;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.tackit.domain.entity.*;
import org.example.tackit.domain.entity.Org.BranchType;
import org.example.tackit.domain.entity.Org.Region;
import org.example.tackit.domain.entity.Org.University;
import org.example.tackit.domain.member.repository.MemberRepository;
import org.example.tackit.domain.university.dto.UniversityRawDto;
import org.example.tackit.domain.university.repository.RegionRepository;
import org.example.tackit.domain.university.repository.UniversityRepository;
import org.example.tackit.global.utils.HangulUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Component
@RequiredArgsConstructor
@Slf4j
public class DataInitializer implements CommandLineRunner {

private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;
private final UniversityRepository universityRepository;
private final RegionRepository regionRepository;
private final ObjectMapper objectMapper;

// commandLineRunner의 run 메서드는 String... args로 유지 (String[] args와 유사) -> 스프링 부트 공식 문서 참고
// args를 꼭 배열로 넘겨야 하는 건 아니고 가변적으로 받을 수 있다는 의도를 나타냄
@Override

Choose a reason for hiding this comment

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

medium

여기서 RuntimeException을 사용하는 대신, OrganizationService에서 사용되는 MemberNotFoundException과 같이 더 구체적인 예외를 사용하거나, ErrorCode를 포함하는 커스텀 예외를 사용하는 것이 좋습니다. 이는 애플리케이션 전반의 예외 처리 일관성을 높이고 클라이언트가 오류를 더 쉽게 이해할 수 있도록 돕습니다.

            .orElseThrow(() -> new MemberNotFoundException(ErrorCode.MEMBER_NOT_FOUND));

public void run(String... args) throws Exception {
// 1. Region 지역 데이터 - 대학 데이터보다 먼저 존재해야 함
initializeRegions();

// 2. University 대학 데이터
initializeUniversities();

Choose a reason for hiding this comment

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

medium

마찬가지로, RuntimeException 대신 UniversityNotFoundException과 같이 더 구체적인 예외를 정의하고 사용하는 것이 좋습니다. 이는 오류의 의미를 명확히 하고 디버깅을 용이하게 합니다.

            .orElseThrow(() -> new UniversityNotFoundException(ErrorCode.UNIVERSITY_NOT_FOUND)); // 예시


// 3. 테스트/관리자 데이터
initializeAdmin();
}

Choose a reason for hiding this comment

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

medium

이곳에서도 RuntimeException 대신 DuplicateOrganizationException과 같이 비즈니스 로직에 맞는 구체적인 예외를 사용하는 것이 좋습니다. 이는 예외 처리 계층을 명확히 하고, 클라이언트에게 더 의미 있는 오류 메시지를 전달할 수 있게 합니다.

            throw new DuplicateOrganizationException(ErrorCode.DUPLICATE_CLUB_NAME_IN_UNIVERSITY); // 예시


private void initializeRegions() {
if (regionRepository.count() == 0) {
List<Region> regions = new ArrayList<>();

Choose a reason for hiding this comment

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

medium

이 경우에도 RuntimeException 대신 DuplicateOrganizationException과 같은 구체적인 예외를 사용하는 것이 좋습니다. 이는 오류 유형을 명확히 하고, 애플리케이션의 견고성을 높입니다.

            throw new DuplicateOrganizationException(ErrorCode.DUPLICATE_COMMUNITY_NAME); // 예시

// 0번: 연합 동아리
regions.add(Region.builder()
.regionId(0)
.regionName("전국(연합)")
.sidoCode("00") // 공공데이터에 없는 가상의 코드
.build());

regions.addAll(List.of(
Region.builder().regionId(1).regionName("서울특별시").sidoCode("11").build(),
Region.builder().regionId(2).regionName("부산광역시").sidoCode("26").build(),
Region.builder().regionId(3).regionName("대구광역시").sidoCode("27").build(),
Region.builder().regionId(4).regionName("인천광역시").sidoCode("28").build(),
Region.builder().regionId(5).regionName("광주광역시").sidoCode("29").build(),
Region.builder().regionId(6).regionName("대전광역시").sidoCode("30").build(),
Region.builder().regionId(7).regionName("울산광역시").sidoCode("31").build(),
Region.builder().regionId(8).regionName("세종특별자치시").sidoCode("36").build(),
Region.builder().regionId(9).regionName("경기도").sidoCode("41").build(),
Region.builder().regionId(10).regionName("강원특별자치도").sidoCode("51").build(),
Region.builder().regionId(11).regionName("충청북도").sidoCode("43").build(),
Region.builder().regionId(12).regionName("충청남도").sidoCode("44").build(),
Region.builder().regionId(13).regionName("전북특별자치도").sidoCode("52").build(),
Region.builder().regionId(14).regionName("전라남도").sidoCode("46").build(),
Region.builder().regionId(15).regionName("경상북도").sidoCode("47").build(),
Region.builder().regionId(16).regionName("경상남도").sidoCode("48").build(),
Region.builder().regionId(17).regionName("제주특별자치도").sidoCode("50").build()
));

regionRepository.saveAll(regions);
log.info("Region data initialized (including Union code 0).");
}
}

private void initializeUniversities() throws Exception {
if (universityRepository.count() > 0) return;

InputStream inputStream = new ClassPathResource("data/university_data.json").getInputStream();
JsonNode records = objectMapper.readTree(inputStream).path("records");
List<University> universityList = new ArrayList<>();

for(JsonNode node : records) {
UniversityRawDto dto = objectMapper.treeToValue(node, UniversityRawDto.class);

if ("대학원".equals(dto.getUniversityType())) continue;

regionRepository.findBySidoCode(dto.getSidoCode()).ifPresent(region -> {
universityList.add(University.builder()
.universityName(dto.getSchoolName())
.universityChosung(HangulUtils.getChosung(dto.getSchoolName()))
.branchType("본교".equals(dto.getBranchType()) ? BranchType.MAIN : BranchType.BRANCH)
.region(region)
.address(dto.getAddress())
.build());
});
}
universityRepository.saveAll(universityList);
log.info("Successfully initialized {} universities.", universityList.size());
}

private void initializeAdmin() {
if (memberRepository.findByEmail("contact.tackit@gmail.com").isEmpty()) {
memberRepository.save(Member.builder()
.email("contact.tackit@gmail.com")
.password(passwordEncoder.encode("admin1"))
.name("관리자")
.activeStatus(ActiveStatus.ACTIVE)
.createdAt(LocalDateTime.now())
.build());
}
}
}
23 changes: 23 additions & 0 deletions src/main/java/org/example/tackit/domain/entity/Org/BranchType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.example.tackit.domain.entity.Org;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum BranchType {
MAIN("본교"),
BRANCH("분교");

private final String description;

// 한글 명칭으로 Enum을 찾아주는 편의 메서드 (데이터 초기화 시 유용)
public static BranchType fromDescription(String description) {
for (BranchType type : BranchType.values()) {
if (type.description.equals(description)) {
return type;
}
}
return MAIN; // 기본값 설정 혹은 에러 처리
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ public class Organization {

// Club 필드
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "school_id")
private School school;
@JoinColumn(name = "university_id")
private University university;
}


24 changes: 24 additions & 0 deletions src/main/java/org/example/tackit/domain/entity/Org/Region.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.example.tackit.domain.entity.Org;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.*;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@Table(name = "region")
public class Region {
@Id
private Integer regionId;

@Column(unique = true, nullable = false, length = 50)
private String regionName;

@Column(unique = true, nullable = false, length = 10)
private String sidoCode;
}
35 changes: 0 additions & 35 deletions src/main/java/org/example/tackit/domain/entity/Org/School.java

This file was deleted.

This file was deleted.

36 changes: 36 additions & 0 deletions src/main/java/org/example/tackit/domain/entity/Org/University.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.example.tackit.domain.entity.Org;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@Table(name = "university", indexes = {
@Index(name = "idx_university_name", columnList = "university_name"),
@Index(name = "idx_university_chosung", columnList = "university_chosung")
})
public class University {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long universityId;

@Column(nullable = false, length = 100)
private String universityName;

@Column(length = 100)
private String universityChosung; // 초성 저장 컬럼

@Enumerated(EnumType.STRING)
@Column(length = 10)
private BranchType branchType;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "region_id", nullable = false)
private Region region;

@Column(length = 255)
private String address;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lombok.RequiredArgsConstructor;
import org.example.tackit.domain.organization.dto.req.OrgCreateReqDto;
import org.example.tackit.domain.organization.dto.req.OrgJoinReqDto;
import org.example.tackit.domain.organization.dto.resp.OrgRespDto;
import org.example.tackit.domain.organization.service.OrganizationService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -18,17 +19,17 @@ public class OrganizationController {

// 모임 등록
@PostMapping
public ResponseEntity<?> createOrg(
public ResponseEntity<OrgRespDto> createOrg(
@RequestBody OrgCreateReqDto dto,
Authentication authentication
) {
if (authentication == null) {
return ResponseEntity.status(401).body("인증 정보가 없습니다.");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}

String email = authentication.getName();
organizationService.createOrg(dto, email);
return ResponseEntity.ok("성공");
OrgRespDto response = organizationService.createOrg(dto, email);
return ResponseEntity.ok(response);
}

// 모임 참여
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import lombok.*;
import org.example.tackit.domain.entity.Org.*;
import org.example.tackit.domain.entity.Org.University;

@Data
@Getter
Expand All @@ -13,14 +14,14 @@ public class OrgCreateReqDto {
private OrgType orgType;
private String orgDescription;

private Long schoolId;
private Long universityId;

public Organization toEntity(School school) {
public Organization toEntity(University university) {
return Organization.builder()
.name(this.orgName)
.type(this.orgType)
.description(this.orgDescription)
.school(this.orgType == OrgType.CLUB ? school : null)
.university(this.orgType == OrgType.CLUB ? university : null)
.build();
}
}
Loading