diff --git a/src/main/java/com/hackathon/momento/auth/api/AuthController.java b/src/main/java/com/hackathon/momento/auth/api/AuthController.java index 44cad9e..301a2c1 100644 --- a/src/main/java/com/hackathon/momento/auth/api/AuthController.java +++ b/src/main/java/com/hackathon/momento/auth/api/AuthController.java @@ -1,6 +1,6 @@ package com.hackathon.momento.auth.api; -import com.hackathon.momento.auth.api.dto.AuthResDto; +import com.hackathon.momento.auth.api.dto.response.AuthResDto; import com.hackathon.momento.global.oauth.KakaoOauthService; import com.hackathon.momento.global.template.RspTemplate; import io.swagger.v3.oas.annotations.Operation; diff --git a/src/main/java/com/hackathon/momento/auth/api/dto/AuthResDto.java b/src/main/java/com/hackathon/momento/auth/api/dto/response/AuthResDto.java similarity index 86% rename from src/main/java/com/hackathon/momento/auth/api/dto/AuthResDto.java rename to src/main/java/com/hackathon/momento/auth/api/dto/response/AuthResDto.java index 7d68752..ac3ee74 100644 --- a/src/main/java/com/hackathon/momento/auth/api/dto/AuthResDto.java +++ b/src/main/java/com/hackathon/momento/auth/api/dto/response/AuthResDto.java @@ -1,4 +1,4 @@ -package com.hackathon.momento.auth.api.dto; +package com.hackathon.momento.auth.api.dto.response; import lombok.Builder; diff --git a/src/main/java/com/hackathon/momento/global/config/OpenAIConfig.java b/src/main/java/com/hackathon/momento/global/config/OpenAIConfig.java new file mode 100644 index 0000000..6a1cd19 --- /dev/null +++ b/src/main/java/com/hackathon/momento/global/config/OpenAIConfig.java @@ -0,0 +1,23 @@ +package com.hackathon.momento.global.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class OpenAIConfig { + + @Value("${openai.api.key}") + private String openAIKey; + + @Bean + public RestTemplate template() { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.getInterceptors().add((request, body, execution) -> { + request.getHeaders().add("Authorization", "Bearer " + openAIKey); + return execution.execute(request, body); + }); + return restTemplate; + } +} diff --git a/src/main/java/com/hackathon/momento/global/config/SecurityConfig.java b/src/main/java/com/hackathon/momento/global/config/SecurityConfig.java index 77b8c32..e13ab0c 100644 --- a/src/main/java/com/hackathon/momento/global/config/SecurityConfig.java +++ b/src/main/java/com/hackathon/momento/global/config/SecurityConfig.java @@ -21,9 +21,9 @@ public class SecurityConfig { private final String[] PERMIT_ALL_URLS = { "swagger-ui/**", "v3/api-docs/**", - "profile", - "api/v1/auth/**", - "api/v1/member/**" + "/", + "/profile", + "/api/v1/**" }; @Bean diff --git a/src/main/java/com/hackathon/momento/global/oauth/KakaoOauthService.java b/src/main/java/com/hackathon/momento/global/oauth/KakaoOauthService.java index e468d4c..46e9269 100644 --- a/src/main/java/com/hackathon/momento/global/oauth/KakaoOauthService.java +++ b/src/main/java/com/hackathon/momento/global/oauth/KakaoOauthService.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.hackathon.momento.auth.api.dto.AuthResDto; +import com.hackathon.momento.auth.api.dto.response.AuthResDto; import com.hackathon.momento.auth.application.TokenRenewService; import com.hackathon.momento.global.jwt.TokenProvider; import com.hackathon.momento.global.oauth.exception.OauthException; diff --git a/src/main/java/com/hackathon/momento/member/api/MemberController.java b/src/main/java/com/hackathon/momento/member/api/MemberController.java index af2900e..8acbb85 100644 --- a/src/main/java/com/hackathon/momento/member/api/MemberController.java +++ b/src/main/java/com/hackathon/momento/member/api/MemberController.java @@ -9,7 +9,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import java.security.Principal; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; @@ -25,6 +24,8 @@ @Tag(name = "사용자 정보", description = "사용자 정보를 담당하는 API 그룹") public class MemberController { + private static final Long TEMP_MEMBER_ID = 1L; // 테스트용 임시 사용자 ID + private final MemberService memberService; @PutMapping("/complete-profile") @@ -37,12 +38,11 @@ public class MemberController { @ApiResponse(responseCode = "500", description = "서버 오류") } ) - public RspTemplate completeProfile( - Principal principal, + public RspTemplate completeProfile( @Valid @RequestBody ProfileReqDto reqDto) { - memberService.completeProfile(principal, reqDto); - return new RspTemplate<>(HttpStatus.OK, "프로필 완성!"); + memberService.completeProfile(TEMP_MEMBER_ID, reqDto); + return new RspTemplate<>(HttpStatus.OK, "프로필 완성 성공"); } @GetMapping("/profile") @@ -55,8 +55,8 @@ public RspTemplate completeProfile( @ApiResponse(responseCode = "500", description = "서버 오류") } ) - public RspTemplate getProfile(Principal principal) { - ProfileResDto profile = memberService.getProfile(principal); + public RspTemplate getProfile() { + ProfileResDto profile = memberService.getProfile(TEMP_MEMBER_ID); return new RspTemplate<>(HttpStatus.OK, "프로필 조회 성공", profile); } @@ -72,10 +72,9 @@ public RspTemplate getProfile(Principal principal) { } ) public RspTemplate updateProfile( - Principal principal, @Valid @RequestBody UpdateProfileReqDto reqDto) { - ProfileResDto updatedProfile = memberService.updateProfile(principal, reqDto); + ProfileResDto updatedProfile = memberService.updateProfile(TEMP_MEMBER_ID, reqDto); return new RspTemplate<>(HttpStatus.OK, "프로필 수정 성공", updatedProfile); } } diff --git a/src/main/java/com/hackathon/momento/member/application/MemberService.java b/src/main/java/com/hackathon/momento/member/application/MemberService.java index d2d92f4..d1d8bcb 100644 --- a/src/main/java/com/hackathon/momento/member/application/MemberService.java +++ b/src/main/java/com/hackathon/momento/member/application/MemberService.java @@ -7,7 +7,6 @@ import com.hackathon.momento.member.domain.repository.MemberRepository; import com.hackathon.momento.member.exception.FirstLoginOnlyException; import com.hackathon.momento.member.exception.MemberNotFoundException; -import java.security.Principal; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -20,8 +19,10 @@ public class MemberService { private final MemberRepository memberRepository; @Transactional - public void completeProfile(Principal principal, ProfileReqDto reqDto) { - Member member = getMemberByPrincipal(principal); + public void completeProfile(Long memberId, ProfileReqDto reqDto) { +// Member member = getMemberByPrincipal(principal); + Member member = memberRepository.findById(memberId) + .orElseThrow(MemberNotFoundException::new); if (!member.isFirstLogin()) { throw new FirstLoginOnlyException(); @@ -31,23 +32,27 @@ public void completeProfile(Principal principal, ProfileReqDto reqDto) { memberRepository.save(member); } - public ProfileResDto getProfile(Principal principal) { - Member member = getMemberByPrincipal(principal); + public ProfileResDto getProfile(Long memberId) { +// Member member = getMemberByPrincipal(principal); + Member member = memberRepository.findById(memberId) + .orElseThrow(MemberNotFoundException::new); return ProfileResDto.from(member); } @Transactional - public ProfileResDto updateProfile(Principal principal, UpdateProfileReqDto reqDto) { - Member member = getMemberByPrincipal(principal); - member.updateProfile(reqDto.name(), reqDto.stack(), reqDto.persona(), reqDto.ability()); + public ProfileResDto updateProfile(Long memberId, UpdateProfileReqDto reqDto) { +// Member member = getMemberByPrincipal(principal); + Member member = memberRepository.findById(memberId) + .orElseThrow(MemberNotFoundException::new); + member.updateProfile(reqDto.name(), reqDto.stack(), reqDto.persona(), reqDto.ability()); return ProfileResDto.from(member); } - private Member getMemberByPrincipal(Principal principal) { - Long memberId = Long.parseLong(principal.getName()); - return memberRepository.findById(memberId) - .orElseThrow(MemberNotFoundException::new); - } +// private Member getMemberByPrincipal(Principal principal) { +// Long memberId = Long.parseLong(principal.getName()); +// return memberRepository.findById(memberId) +// .orElseThrow(MemberNotFoundException::new); +// } } diff --git a/src/main/java/com/hackathon/momento/team/api/TeamController.java b/src/main/java/com/hackathon/momento/team/api/TeamController.java new file mode 100644 index 0000000..2f72a8d --- /dev/null +++ b/src/main/java/com/hackathon/momento/team/api/TeamController.java @@ -0,0 +1,44 @@ +package com.hackathon.momento.team.api; + +import com.hackathon.momento.global.template.RspTemplate; +import com.hackathon.momento.team.api.dto.request.TeamBuildingReqDto; +import com.hackathon.momento.team.application.TeamService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/team") +@Tag(name = "팀", description = "팀을 담당하는 API 그룹") +public class TeamController { + + private static final Long TEMP_MEMBER_ID = 1L; // 테스트용 임시 사용자 ID + + private final TeamService teamService; + + @PostMapping("/building") + @Operation( + summary = "팀 빌딩 정보 저장", + description = "사용자가 팀 빌딩을 위해 필요한 정보들을 입력합니다.", + responses = { + @ApiResponse(responseCode = "200", description = "팀 빌딩 정보 저장 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청"), + @ApiResponse(responseCode = "500", description = "서버 오류") + } + ) + public RspTemplate saveTeamBuilding( +// Principal principal, + @Valid @RequestBody TeamBuildingReqDto reqDto) { + + teamService.saveTeamBuilding(TEMP_MEMBER_ID, reqDto); + return new RspTemplate<>(HttpStatus.OK, "팀 빌딩 정보 저장 성공"); + } +} diff --git a/src/main/java/com/hackathon/momento/team/api/dto/request/TeamBuildingReqDto.java b/src/main/java/com/hackathon/momento/team/api/dto/request/TeamBuildingReqDto.java new file mode 100644 index 0000000..a2e8207 --- /dev/null +++ b/src/main/java/com/hackathon/momento/team/api/dto/request/TeamBuildingReqDto.java @@ -0,0 +1,43 @@ +package com.hackathon.momento.team.api.dto.request; + +import com.hackathon.momento.member.domain.Member; +import com.hackathon.momento.team.domain.Status; +import com.hackathon.momento.team.domain.TeamBuilding; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; +import org.springframework.format.annotation.DateTimeFormat; + +public record TeamBuildingReqDto( + @NotNull(message = "프로젝트 시작 날짜는 필수 입력 항목입니다") + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate startDate, + + @NotNull(message = "프로젝트 종료 날짜는 필수 입력 항목입니다") + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate endDate, + + @Min(value = 2, message = "팀 인원 수는 최소 2명이어야 합니다") + @Max(value = 6, message = "팀 인원 수는 최대 6명이어야 합니다") + int teamSize, + + @NotBlank(message = "본인 포지션은 필수 입력 항목입니다") + String myPosition, + + @NotBlank(message = "요구 포지션 조합은 필수 입력 항목입니다") + String positionCombination +) { + public TeamBuilding toEntity(Member member) { + return TeamBuilding.builder() + .startDate(this.startDate) + .endDate(this.endDate) + .teamSize(this.teamSize) + .myPosition(this.myPosition) + .positionCombination(this.positionCombination) + .status(Status.PENDING) + .member(member) + .build(); + } +} diff --git a/src/main/java/com/hackathon/momento/team/application/TeamService.java b/src/main/java/com/hackathon/momento/team/application/TeamService.java new file mode 100644 index 0000000..1de0d5d --- /dev/null +++ b/src/main/java/com/hackathon/momento/team/application/TeamService.java @@ -0,0 +1,34 @@ +package com.hackathon.momento.team.application; + +import com.hackathon.momento.member.domain.Member; +import com.hackathon.momento.member.domain.repository.MemberRepository; +import com.hackathon.momento.member.exception.MemberNotFoundException; +import com.hackathon.momento.team.api.dto.request.TeamBuildingReqDto; +import com.hackathon.momento.team.domain.Status; +import com.hackathon.momento.team.domain.repository.TeamBuildingRepository; +import com.hackathon.momento.team.exception.TeamBuildingConflictException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class TeamService { + + private final TeamBuildingRepository teamBuildingRepository; + private final MemberRepository memberRepository; + + @Transactional + public void saveTeamBuilding(Long memberId, TeamBuildingReqDto reqDto) { +// Long memberId = Long.parseLong(principal.getName()); + Member member = memberRepository.findById(memberId) + .orElseThrow(MemberNotFoundException::new); + + if (teamBuildingRepository.existsByMemberAndStatus(member, Status.PENDING)) { + throw new TeamBuildingConflictException(); + } + + teamBuildingRepository.save(reqDto.toEntity(member)); + } +} diff --git a/src/main/java/com/hackathon/momento/team/domain/Status.java b/src/main/java/com/hackathon/momento/team/domain/Status.java index 4c9e52b..0c60ae6 100644 --- a/src/main/java/com/hackathon/momento/team/domain/Status.java +++ b/src/main/java/com/hackathon/momento/team/domain/Status.java @@ -1,5 +1,5 @@ package com.hackathon.momento.team.domain; public enum Status { - PENDING, COMPLETED + PENDING, RETRY, COMPLETED } diff --git a/src/main/java/com/hackathon/momento/team/domain/TeamBuilding.java b/src/main/java/com/hackathon/momento/team/domain/TeamBuilding.java index 796d35c..4837f77 100644 --- a/src/main/java/com/hackathon/momento/team/domain/TeamBuilding.java +++ b/src/main/java/com/hackathon/momento/team/domain/TeamBuilding.java @@ -1,13 +1,17 @@ package com.hackathon.momento.team.domain; import com.hackathon.momento.global.entity.BaseEntity; +import com.hackathon.momento.member.domain.Member; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import java.time.LocalDate; import lombok.AccessLevel; import lombok.Builder; @@ -34,18 +38,32 @@ public class TeamBuilding extends BaseEntity { private int teamSize; @Column(nullable = false) - private String position; + private String myPosition; + + @Column(nullable = false, length = 1024) + private String positionCombination; @Enumerated(EnumType.STRING) @Column(nullable = false) private Status status; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "team_info_id") + private TeamInfo teamInfo; + @Builder - private TeamBuilding(LocalDate startDate, LocalDate endDate, int teamSize, String position, Status status) { + private TeamBuilding(LocalDate startDate, LocalDate endDate, int teamSize, String myPosition, + String positionCombination, Status status, Member member) { this.startDate = startDate; this.endDate = endDate; this.teamSize = teamSize; - this.position = position; + this.myPosition = myPosition; + this.positionCombination = positionCombination; this.status = status; + this.member = member; } } diff --git a/src/main/java/com/hackathon/momento/team/domain/TeamBuildingMember.java b/src/main/java/com/hackathon/momento/team/domain/TeamBuildingMember.java deleted file mode 100644 index c154278..0000000 --- a/src/main/java/com/hackathon/momento/team/domain/TeamBuildingMember.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.hackathon.momento.team.domain; - -import com.hackathon.momento.member.domain.Member; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Entity -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class TeamBuildingMember { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "team_building_member_id") - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "team_building_id", nullable = false) - private TeamBuilding teamBuilding; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id", nullable = false) - private Member member; - - @Builder - private TeamBuildingMember(TeamBuilding teamBuilding, Member member) { - this.teamBuilding = teamBuilding; - this.member = member; - } -} diff --git a/src/main/java/com/hackathon/momento/team/domain/TeamInfo.java b/src/main/java/com/hackathon/momento/team/domain/TeamInfo.java index 02543f3..de4fe07 100644 --- a/src/main/java/com/hackathon/momento/team/domain/TeamInfo.java +++ b/src/main/java/com/hackathon/momento/team/domain/TeamInfo.java @@ -1,14 +1,15 @@ package com.hackathon.momento.team.domain; import com.hackathon.momento.global.entity.BaseEntity; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import java.util.ArrayList; +import java.util.List; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -27,13 +28,11 @@ public class TeamInfo extends BaseEntity { @Column(nullable = false) private String teamName; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "team_building_id") - private TeamBuilding teamBuilding; + @OneToMany(mappedBy = "teamInfo", cascade = CascadeType.ALL, orphanRemoval = true) + private List teamBuildings = new ArrayList<>(); @Builder - private TeamInfo(String teamName, TeamBuilding teamBuilding) { + private TeamInfo(String teamName) { this.teamName = teamName; - this.teamBuilding = teamBuilding; } } diff --git a/src/main/java/com/hackathon/momento/team/domain/TeamInfoMember.java b/src/main/java/com/hackathon/momento/team/domain/TeamInfoMember.java deleted file mode 100644 index 0ab1b65..0000000 --- a/src/main/java/com/hackathon/momento/team/domain/TeamInfoMember.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.hackathon.momento.team.domain; - -import com.hackathon.momento.member.domain.Member; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Entity -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class TeamInfoMember { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "team_member_id") - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "team_info_id", nullable = false) - private TeamInfo teamInfo; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id", nullable = false) - private Member member; - - @Builder - private TeamInfoMember(TeamInfo teamInfo, Member member) { - this.teamInfo = teamInfo; - this.member = member; - } -} diff --git a/src/main/java/com/hackathon/momento/team/domain/repository/TeamBuildingRepository.java b/src/main/java/com/hackathon/momento/team/domain/repository/TeamBuildingRepository.java new file mode 100644 index 0000000..0f54bbd --- /dev/null +++ b/src/main/java/com/hackathon/momento/team/domain/repository/TeamBuildingRepository.java @@ -0,0 +1,13 @@ +package com.hackathon.momento.team.domain.repository; + +import com.hackathon.momento.member.domain.Member; +import com.hackathon.momento.team.domain.Status; +import com.hackathon.momento.team.domain.TeamBuilding; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TeamBuildingRepository extends JpaRepository { + + boolean existsByMemberAndStatus(Member member, Status status); +} diff --git a/src/main/java/com/hackathon/momento/team/domain/repository/TeamInfoRepository.java b/src/main/java/com/hackathon/momento/team/domain/repository/TeamInfoRepository.java new file mode 100644 index 0000000..19c3a77 --- /dev/null +++ b/src/main/java/com/hackathon/momento/team/domain/repository/TeamInfoRepository.java @@ -0,0 +1,9 @@ +package com.hackathon.momento.team.domain.repository; + +import com.hackathon.momento.team.domain.TeamInfo; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TeamInfoRepository extends JpaRepository { +} diff --git a/src/main/java/com/hackathon/momento/team/exception/TeamBuildingConflictException.java b/src/main/java/com/hackathon/momento/team/exception/TeamBuildingConflictException.java new file mode 100644 index 0000000..c0d6bcb --- /dev/null +++ b/src/main/java/com/hackathon/momento/team/exception/TeamBuildingConflictException.java @@ -0,0 +1,13 @@ +package com.hackathon.momento.team.exception; + +import com.hackathon.momento.global.error.exception.ConflictGroupException; + +public class TeamBuildingConflictException extends ConflictGroupException { + public TeamBuildingConflictException(String message) { + super(message); + } + + public TeamBuildingConflictException() { + this("이미 진행 중인 팀 빌딩 요청이 있습니다."); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index affe64a..419a2f9 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -37,4 +37,10 @@ oauth: client-id: ${KAKAO_CLIENT_ID} redirect-uri: ${KAKAO_REDIRECT_URI} admin-key: ${KAKAO_ADMIN_KEY} - token-url: ${KAKAO_TOKEN_URL} \ No newline at end of file + token-url: ${KAKAO_TOKEN_URL} + +openai: + model: ${OPEN_AI_MODEL} + api: + key: ${OPEN_AI_API_KEY} + url: ${OPEN_AI_API_URL} \ No newline at end of file