-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
야간잔류 신청 기능 구현 #26
야간잔류 신청 기능 구현 #26
Changes from 1 commit
a608539
6d08133
2cdb954
85f0e25
d04b675
4586e4f
8f40ecf
7e6af98
dfaf8ae
d679f52
f940741
6845361
a1c7807
3e889da
0deb19c
e3d773b
705f650
32ab2e2
691dbae
441e3f9
04cbe5c
06b219e
506cd43
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.kyonggi.teampu.domain.application.controller; | ||
|
||
import com.kyonggi.teampu.domain.application.dto.ApplicationRequest; | ||
import com.kyonggi.teampu.domain.application.dto.ApplicationResponse; | ||
import com.kyonggi.teampu.domain.application.service.ApplicationService; | ||
import com.kyonggi.teampu.global.response.ApiResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
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/apply") | ||
public class ApplicationController { | ||
private final ApplicationService applicationService; | ||
|
||
@PostMapping | ||
public ResponseEntity<ApiResponse<ApplicationResponse>> createApplication(@RequestBody ApplicationRequest applicationRequest) { | ||
// 400 에러 처리 | ||
if (applicationRequest.getMemberId() == null || applicationRequest.getStartTime() == null || applicationRequest.getEndTime() == null) { | ||
return ResponseEntity | ||
.status(HttpStatus.BAD_REQUEST) | ||
.body(new ApiResponse<>( | ||
new ApiResponse.Status(HttpStatus.BAD_REQUEST, "필수 값이 누락되었습니다"), | ||
null | ||
)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 따로 메시지를 첨부하려하니 관련 메서드가 없어서 구조가 이상해지는 것 같네요. 이 부분은 추가하겠습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 간단하게 추가해주세요! 더 필요한 부분이 있다면 제가 나중에 추가하겠습니다. |
||
} | ||
|
||
try { | ||
ApplicationResponse applicationResponse = applicationService.createApplication(applicationRequest); | ||
return ResponseEntity.ok(new ApiResponse<>( | ||
new ApiResponse.Status(HttpStatus.CREATED, "신청이 완료되었습니다"), | ||
applicationResponse | ||
)); | ||
} catch (IllegalArgumentException e) { | ||
// 401 에러 처리 | ||
return ResponseEntity | ||
.status(HttpStatus.UNAUTHORIZED) | ||
.body(new ApiResponse<>( | ||
new ApiResponse.Status(HttpStatus.UNAUTHORIZED, "권한이 없습니다"), | ||
null | ||
)); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. try-catch 문을 반복적으로 사용해서 코드가 조금 길어지는 것 같습니다! 이 부분은 예외 상황에서 Http 응답이 아닌 예외만 반환하게 하고, 추후에 전역 예외 핸들러를 통해 통일된 Http 응답 형식으로 반환하는 방식으로 수정할 수 있을 것 같습니다. @ExceptionHandler(RuntimeException.class)
public ApiResponse<Void> handle(RuntimeException exception, HttpServletRequest request) {
logInfo(exception, request);
return ApiResponse.exception(exception);
} 요런 느낌인데 나중에 시간나면 추가해볼게유 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 좋습니다! 일단 해당 코드는 주석 처리하고 나중에 예외 throw 하는 방식으로 바꾸겠습니다. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.kyonggi.teampu.domain.application.dto; | ||
|
||
import com.kyonggi.teampu.domain.application.domain.Application; | ||
import com.kyonggi.teampu.domain.application.domain.ApplicationStatus; | ||
import com.kyonggi.teampu.domain.member.domain.Member; | ||
import com.kyonggi.teampu.domain.member.domain.MemberType; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
import java.time.LocalDate; | ||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
public class ApplicationRequest { | ||
private String name; | ||
private MemberType type; | ||
private Long memberId; | ||
private String loginId; | ||
private String phoneNumber; | ||
private String email; | ||
private Integer participantCount; | ||
private List<String> coParticipantNames; | ||
private Boolean privacyAgreement; | ||
private LocalDateTime startTime; | ||
private LocalDateTime endTime; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 회원이 로그인한 상태로 야간잔류를 신청한다는 걸 감안하면 request dto에서 신청자의 정보를 받을 필요 없이 현재 로그인된 회원의 정보를 그대로 가져올 수 있어서, 관련 필드 변수들을 빼도 될 것 같습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 수정하겠습니다. |
||
|
||
public Application toEntity(Member member) { | ||
return Application.builder() | ||
.member(member) | ||
.participantCount(participantCount) | ||
.coParticipantNames(coParticipantNames) | ||
.privacyAgreement(privacyAgreement) | ||
.startTime(startTime) | ||
.endTime(endTime) | ||
.appliedDate(LocalDate.now()) | ||
.status(ApplicationStatus.PENDING) | ||
.build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.kyonggi.teampu.domain.application.dto; | ||
|
||
import com.kyonggi.teampu.domain.application.domain.Application; | ||
import com.kyonggi.teampu.domain.member.domain.MemberType; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
|
||
@Getter | ||
@Builder | ||
@AllArgsConstructor | ||
public class ApplicationResponse { | ||
private String loginId; | ||
private String name; | ||
private MemberType type; | ||
private Long memberId; | ||
private String phoneNumber; | ||
private String email; | ||
private Integer participantCount; | ||
private List<String> coParticipantNames; | ||
private Boolean privacyAgreement; | ||
private LocalDateTime startTime; | ||
private LocalDateTime endTime; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기서 회원 관련 정보는 히원 정보 DTO로 묶으면 좋을 것 같고, coParticipantNames도 단순 이름이 아닌 회원 정보 DTO 내용을 담으면 좋을 것 같습니다! 또한 participantCount의 경우, coParticipantNames.size() + 1로 자체 계산할 수 있을 것 같습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 수정하겠습니다! 감사합니다 |
||
|
||
public static ApplicationResponse of(Application application) { | ||
return ApplicationResponse.builder() | ||
.loginId(application.getMember().getLoginId()) | ||
.name(application.getMember().getName()) | ||
.type(application.getMember().getType()) | ||
.memberId(application.getMember().getId()) | ||
.phoneNumber(application.getMember().getPhoneNumber()) | ||
.email(application.getMember().getEmail()) | ||
.participantCount(application.getParticipantCount()) | ||
.coParticipantNames(application.getCoParticipantNames()) | ||
.privacyAgreement(application.getPrivacyAgreement()) | ||
.startTime(application.getStartTime()) | ||
.endTime(application.getEndTime()) | ||
.build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.kyonggi.teampu.domain.application.repository; | ||
|
||
import com.kyonggi.teampu.domain.application.domain.Application; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface ApplicationRepository extends JpaRepository<Application, Long> { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.kyonggi.teampu.domain.application.service; | ||
|
||
import com.kyonggi.teampu.domain.application.domain.Application; | ||
import com.kyonggi.teampu.domain.application.dto.ApplicationRequest; | ||
import com.kyonggi.teampu.domain.application.dto.ApplicationResponse; | ||
import com.kyonggi.teampu.domain.application.repository.ApplicationRepository; | ||
import com.kyonggi.teampu.domain.member.domain.Member; | ||
import com.kyonggi.teampu.domain.member.repository.MemberRepository; | ||
import jakarta.transaction.Transactional; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class ApplicationService { | ||
private final ApplicationRepository applicationRepository; | ||
private final MemberRepository memberRepository; | ||
|
||
@Transactional | ||
public ApplicationResponse createApplication(ApplicationRequest request) { | ||
Member member = memberRepository.findById(request.getMemberId()) | ||
.orElseThrow(() -> new IllegalArgumentException("회원을 찾을 수 없습니다")); | ||
|
||
Application application = request.toEntity(member); | ||
Application savedApplication = applicationRepository.save(application); | ||
ApplicationResponse applicationResponse = ApplicationResponse.of(savedApplication); | ||
|
||
return ApplicationResponse.of(applicationRepository.save(application)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.kyonggi.teampu.domain.member.repository; | ||
|
||
import com.kyonggi.teampu.domain.member.domain.Member; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface MemberRepository extends JpaRepository<Member, Long> { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,58 @@ | ||
package com.kyonggi.teampu.global.config; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.authentication.AuthenticationManager; | ||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; | ||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
import org.springframework.web.cors.CorsConfiguration; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
@Configuration | ||
@EnableWebSecurity | ||
@RequiredArgsConstructor | ||
public class SecurityConfig { | ||
private static final String[] PUBLIC_URLS = { | ||
"/**" | ||
}; | ||
|
||
@Bean | ||
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { | ||
return configuration.getAuthenticationManager(); | ||
} | ||
|
||
@Bean | ||
public BCryptPasswordEncoder bCryptPasswordEncoder() { | ||
return new BCryptPasswordEncoder(); | ||
} | ||
|
||
@Bean | ||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
//CORS 설정 | ||
http.cors((cors) -> cors | ||
.configurationSource(request -> { | ||
CorsConfiguration configuration = new CorsConfiguration(); | ||
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000", "http://localhost:8080")); | ||
configuration.setAllowedMethods(Collections.singletonList("*")); | ||
configuration.setAllowCredentials(true); | ||
configuration.setAllowedHeaders(Collections.singletonList("*")); | ||
configuration.setMaxAge(3600L); | ||
configuration.setExposedHeaders(List.of("Authorization")); | ||
return configuration; | ||
})); | ||
|
||
http.csrf(AbstractHttpConfigurer::disable); | ||
http.formLogin(AbstractHttpConfigurer::disable); | ||
http.httpBasic(AbstractHttpConfigurer::disable); | ||
|
||
return http.build(); | ||
} | ||
} | ||
//package com.kyonggi.teampu.global.config; | ||
// | ||
//import lombok.RequiredArgsConstructor; | ||
//import org.springframework.context.annotation.Bean; | ||
//import org.springframework.context.annotation.Configuration; | ||
//import org.springframework.security.authentication.AuthenticationManager; | ||
//import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; | ||
//import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
//import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
//import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; | ||
//import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
//import org.springframework.security.web.SecurityFilterChain; | ||
//import org.springframework.web.cors.CorsConfiguration; | ||
// | ||
//import java.util.Arrays; | ||
//import java.util.Collections; | ||
//import java.util.List; | ||
// | ||
//@Configuration | ||
//@EnableWebSecurity | ||
//@RequiredArgsConstructor | ||
//public class SecurityConfig { | ||
// private static final String[] PUBLIC_URLS = { | ||
// "/**" | ||
// }; | ||
// | ||
// @Bean | ||
// public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { | ||
// return configuration.getAuthenticationManager(); | ||
// } | ||
// | ||
// @Bean | ||
// public BCryptPasswordEncoder bCryptPasswordEncoder() { | ||
// return new BCryptPasswordEncoder(); | ||
// } | ||
// | ||
// @Bean | ||
// public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
// //CORS 설정 | ||
// http.cors((cors) -> cors | ||
// .configurationSource(request -> { | ||
// CorsConfiguration configuration = new CorsConfiguration(); | ||
// configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000", "http://localhost:8080")); | ||
// configuration.setAllowedMethods(Collections.singletonList("*")); | ||
// configuration.setAllowCredentials(true); | ||
// configuration.setAllowedHeaders(Collections.singletonList("*")); | ||
// configuration.setMaxAge(3600L); | ||
// configuration.setExposedHeaders(List.of("Authorization")); | ||
// return configuration; | ||
// })); | ||
// | ||
// http.csrf(AbstractHttpConfigurer::disable); | ||
// http.formLogin(AbstractHttpConfigurer::disable); | ||
// http.httpBasic(AbstractHttpConfigurer::disable); | ||
// | ||
// return http.build(); | ||
// } | ||
//} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,8 @@ | ||
spring: | ||
h2: | ||
console: | ||
enabled: true | ||
path: /h2-console | ||
datasource: | ||
url: jdbc:h2:mem:test | ||
username: sa | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ResponseEntity와 ApiResponse는 거의 같은 기능이어서 한 번 더 감싸지 않아도 될 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵! 좋습니다. ApiResponse에 상태 코드별 메서드를 만들면 더 간단하게 구현할 수 있을 것 같습니다.