Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ public class AuthService {
private final S3Util s3Util;


/**
* Create a new user from the provided sign-up data, persist it, and upload an optional profile image.
*
* Validates the request (email uniqueness and birthday), resolves the user's region, saves the new user,
* and uploads the profile image if one is provided.
*
* @param request DTO containing the sign-up data (name, email, password, birthday, phone number, regionId, career)
* @param profileImage optional profile image file to associate with the new user; may be null or empty
*/
@Transactional
public void signUp(SignUpRequestDto request, MultipartFile profileImage) {

Expand Down Expand Up @@ -105,6 +114,14 @@ public GoogleLoginResponseDto googleLogin(GoogleLoginRequestDto request) {
: generateGoogleLoginTokens(user);
}

/**
* Completes a pending Google-authenticated user's signup, updates their profile, uploads an optional profile image, and returns login tokens.
*
* @param request DTO carrying signup fields (name, birthday, phoneNumber, regionId, career) to apply to the user
* @param user the existing pending User to be updated and activated
* @param profileImage optional profile image file to upload; ignored if null or empty
* @return a LoginUserResponseDto containing the generated access and refresh tokens for the user
*/
@Transactional
public LoginUserResponseDto googleSignup(GoogleSignUpRequestDto request, User user, MultipartFile profileImage) {

Expand Down Expand Up @@ -207,4 +224,4 @@ private void uploadProfileImage(User user, MultipartFile profileImage) {
user.updateProfile(key);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@
@Tag(name = "Region", description = "지역 검색 API")
public interface RegionApi {

/**
* Searches regions by a keyword across all address levels and returns matching results.
*
* The search is case-insensitive and supports partial matches (e.g., "서울", "종로", "청운동").
*
* @param searchKeyword the keyword to search for in addresses (city/province, district, neighborhood, etc.)
* @param page zero-based page index to retrieve; defaults to 0 when not specified
* @return a paged response containing a list of matching SearchRegionDto entries and pagination metadata
*/
@Operation(
summary = "지역 검색",
description = """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@ public class RegionController implements RegionApi {

private final RegionQueryService regionQueryService;

/**
* Searches regions matching the provided keyword and returns a paginated result.
*
* @param searchKeyword keyword used to filter regions
* @param page zero-based page index (defaults to 0)
* @return a page of SearchRegionDto objects with pagination metadata
*/
@Override
@GetMapping
public PageResponseDto<SearchRegionDto> searchRegion (@RequestParam String searchKeyword,
@RequestParam(defaultValue = "0") Integer page) {
return regionQueryService.searchRegion(searchKeyword, page);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ public record SearchRegionDto(

String fullAddress
) {
/**
* Create a SearchRegionDto from a Region domain object.
*
* @param region the Region to convert
* @return a SearchRegionDto containing the region's id and full address
*/
public static SearchRegionDto from(Region region) {
return new SearchRegionDto(region.getId(), region.getFullAddress());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
import java.util.Optional;

public interface RegionRepository extends JpaRepository<Region, Long> {
/**
* Searches for regions whose `searchKeywords` contain the given term, matching case-insensitively, and returns results according to the provided pagination.
*
* @param keyword the substring to match against Region.searchKeywords (case-insensitive)
* @param pageable pagination and sorting information for the query results
* @return a Page of Region entities whose searchKeywords contain `keyword`, respecting the supplied Pageable
*/
@Query("SELECT r FROM Region r WHERE LOWER(r.searchKeywords) LIKE LOWER(CONCAT('%', :keyword, '%'))")
Page<Region> searchByKeywordIgnoreCase(@Param("keyword") String keyword, Pageable pageable);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,25 @@
public class RegionQueryService {
private final RegionRepository regionRepository;

/**
* Retrieve the Region with the given identifier.
*
* @param regionId the identifier of the region to retrieve
* @return the Region matching the provided identifier
* @throws BaseException if no region exists for the given identifier (RegionErrorCode.REGION_NOT_FOUND)
*/
public Region findRegionById(Long regionId) {
return regionRepository.findById(regionId)
.orElseThrow(() -> BaseException.type(RegionErrorCode.REGION_NOT_FOUND));
}

/**
* Searches for regions whose properties match the given keyword (case-insensitive) and returns paginated results.
*
* @param keyword the search term to match against regions; leading and trailing whitespace is ignored
* @param page zero-based page index to return
* @return a page of SearchRegionDto representing matching regions for the requested page
*/
public PageResponseDto<SearchRegionDto> searchRegion(String keyword, Integer page){

String trimKeyword = keyword.trim();
Expand All @@ -32,4 +46,4 @@ public PageResponseDto<SearchRegionDto> searchRegion(String keyword, Integer pag
return PageResponseDto.from(regionRepository.searchByKeywordIgnoreCase(trimKeyword, pageable)
.map(SearchRegionDto::from));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ public record GetUserInfoResponseDto(
String imageUrl
) {

/**
* Create a GetUserInfoResponseDto populated from the given User and image URL.
*
* @param user the User entity whose fields (name, email, birthday, phoneNumber, career, region) are copied into the DTO
* @param imageUrl the image URL to assign to the DTO
* @return a GetUserInfoResponseDto containing the user's information and the provided image URL
*/
public static GetUserInfoResponseDto of(User user, String imageUrl) {
return GetUserInfoResponseDto.builder()
.name(user.getName())
Expand All @@ -41,4 +48,4 @@ public static GetUserInfoResponseDto of(User user, String imageUrl) {
.build();
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ public void deleteUser(User user){
user.softDelete();
}

/**
* Updates the given user's profile fields (name, phone number, birthday, region, and career) from the provided request.
*
* The request birthday is validated and the region is resolved by its id before the user's data is updated.
*
* @param user the user to update
* @param request DTO containing the new profile values; `birthday` must be an ISO-8601 date string and `regionId` must refer to an existing region
*/
@Transactional
public void updateUserInfo(User user, UpdateUserInfoRequestDto request) {

Expand Down Expand Up @@ -91,4 +99,4 @@ public void deleteProfileImage(User user) {
}
user.deleteProfile();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@
@EnableWebSecurity
public class SecurityConfig {

/**
* Configure and build the application's HTTP security filter chain.
*
* Configures CSRF disabled, CORS via corsConfigurationSource(), stateless session management,
* form login and HTTP Basic disabled, URL-based authorization rules (including public endpoints,
* role requirements for Google login completion, admin and user routes), custom authentication
* entry point handling, and registers a JWT authentication filter before the username/password filter.
*
* @param http the HttpSecurity to configure
* @param jwtUtil utility used by the JWT authentication filter to validate and parse tokens
* @param customAuthenticationEntryPoint handler invoked on authentication failures
* @return the configured SecurityFilterChain
*/
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http,
JwtUtil jwtUtil,
Expand Down Expand Up @@ -115,4 +128,4 @@ public CorsConfigurationSource corsConfigurationSource() {
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ public record PageResponseDto<T>(

boolean hasNext
) {
/**
* Create a PageResponseDto from a Spring Data Page.
*
* @param page the Spring Data Page to convert
* @param <T> the element type of the page content
* @return a PageResponseDto containing the page's content, the page number as currentPage,
* the total number of pages as totalPage, the total number of elements, and whether a next page exists
*/
public static <T> PageResponseDto<T> from(Page<T> page) {
return new PageResponseDto<>(
page.getContent(),
Expand All @@ -24,4 +32,4 @@ public static <T> PageResponseDto<T> from(Page<T> page) {
page.hasNext()
);
}
}
}