Skip to content

MOSU-273 feat: 등록된 전화번호 확인 api 구현#274

Merged
wlgns12370 merged 8 commits intodevelopfrom
feature/mosu-273
Aug 10, 2025
Merged

MOSU-273 feat: 등록된 전화번호 확인 api 구현#274
wlgns12370 merged 8 commits intodevelopfrom
feature/mosu-273

Conversation

@wlgns12370
Copy link
Contributor

@wlgns12370 wlgns12370 commented Aug 10, 2025

✨ 구현한 기능

📢 논의하고 싶은 내용

  • x

🎸 기타

  • x

Summary by CodeRabbit

  • New Features

    • Added a new API endpoint to check if a phone number is available for registration.
    • Introduced a response format for phone number availability status.
  • Improvements

    • User existence checks now consider both phone number and login ID for enhanced validation during sign-up and OAuth processes.
    • Updated whitelist to include new endpoints for phone number and customer key checks.
  • Bug Fixes

    • Corrected import locations for response DTOs to ensure consistency.

@wlgns12370 wlgns12370 self-assigned this Aug 10, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 10, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Warning

Rate limit exceeded

@wlgns12370 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 17 minutes and 9 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 445393a and 0373d2b.

📒 Files selected for processing (2)
  • src/main/java/life/mosu/mosuserver/global/util/CookieBuilderUtil.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/UserController.java (2 hunks)

Walkthrough

This update introduces a new API endpoint to check if a phone number is already registered. It adds phone number existence checks to user registration and OAuth flows, creates a response DTO for phone number availability, and updates whitelisting. Supporting repository and service methods for phone number lookups are also included.

Changes

Cohort / File(s) Change Summary
Phone Number Existence Logic
src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java, src/main/java/life/mosu/mosuserver/application/oauth/OAuthUserPersistenceProcessor.java
User existence checks now consider both phone number and login ID during registration and OAuth user processing.
User Phone Number API
src/main/java/life/mosu/mosuserver/presentation/user/UserController.java, src/main/java/life/mosu/mosuserver/application/user/UserService.java, src/main/java/life/mosu/mosuserver/domain/user/repository/UserJpaRepository.java, src/main/java/life/mosu/mosuserver/presentation/user/dto/response/IsPhoneNumberAvailableResponse.java
Adds a new GET endpoint /user/check-phone-number with service and repository support, and a response DTO for availability.
Whitelist Update
src/main/java/life/mosu/mosuserver/global/filter/Whitelist.java
Adds whitelist entries for the new phone number check and customer key check endpoints, allowing GET requests.
DTO Package Refactor
src/main/java/life/mosu/mosuserver/presentation/user/dto/response/IsLoginIdAvailableResponse.java, src/main/java/life/mosu/mosuserver/presentation/user/UserControllerDocs.java
Moves IsLoginIdAvailableResponse from request to response package and updates related imports.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant UserController
    participant UserService
    participant UserJpaRepository

    Client->>UserController: GET /user/check-phone-number?phoneNumber=...
    UserController->>UserService: isPhoneNumberAvailable(phoneNumber)
    UserService->>UserJpaRepository: existsByPhoneNumber(phoneNumber)
    UserJpaRepository-->>UserService: Boolean
    UserService-->>UserController: Boolean
    UserController-->>Client: ApiResponseWrapper<IsPhoneNumberAvailableResponse>
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Assessment against linked issues

Objective Addressed Explanation
등록된 전화번호 확인 api 구현 (#273)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Possibly related PRs

Suggested labels

우선순위 중

Poem

A hop and a leap, a new check appears,
To see if your phone is already here.
With whitelists updated and endpoints anew,
The server now answers, "Is this number for you?"
🐇✨
Review with delight—this code is all right!

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/mosu-273

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @wlgns12370, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This PR introduces a new API endpoint to check the availability of a phone number during user registration or similar processes. It also refactors existing user existence checks to prioritize phone numbers over login IDs/emails, indicating a shift in how user uniqueness is managed within the system.

Highlights

  • New Phone Number Availability API: I've implemented a new GET endpoint /api/v1/user/check-phoneNumber in UserController to allow clients to verify if a given phone number is already registered.
  • Core Logic for Phone Number Check: The UserService now includes a isPhoneNumberAvailable method, which leverages a new existsByPhoneNumber query in UserJpaRepository to efficiently check for existing phone numbers.
  • Shift to Phone Number for User Uniqueness: I've updated the SignUpAccountStepProcessor and OAuthUserPersistenceProcessor to use phone numbers for checking user existence, moving away from loginId or email for this purpose.
  • DTO Refactoring: The IsLoginIdAvailableResponse DTO has been moved to the dto.response package, and a new IsPhoneNumberAvailableResponse DTO has been created to handle the response for the new phone number availability check.
  • Improved Error Handling: The Gender enum now throws a more specific CustomRuntimeException with ErrorCode.INVALID_GENDER when an invalid gender name is provided, enhancing error clarity.
  • API Whitelisting: The new /api/v1/user/check-phoneNumber endpoint has been added to the application's security whitelist to ensure it's publicly accessible.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements an API to check for the availability of a phone number. It includes adding the necessary service method, repository method, controller endpoint, and whitelisting the new endpoint. The changes also include some good refactorings, such as using custom exceptions instead of IllegalArgumentException and moving a DTO to a more appropriate package.

However, I've identified two significant issues in the user lookup logic. First, in the sign-up process, the check for loginId uniqueness has been removed, which could lead to database errors. Second, in the OAuth process, there's a critical inconsistency between how existing users are found (by phone number) and how new users are created (using email as loginId), which will cause runtime exceptions. I've also included a couple of suggestions to improve REST API path consistency.

@Transactional
public UserJpaEntity process(final OAuthUserInfo info) {
return userRepository.findByLoginId(info.email())
return userRepository.findByPhoneNumber(info.phoneNumber())

Choose a reason for hiding this comment

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

critical

The logic for finding an existing OAuth user has been changed to use phoneNumber. However, when creating a new user, loginId is still set to info.email(), and loginId has a unique constraint in the database. This creates an inconsistency that can lead to critical errors.

For example, if a user already exists with a certain email, but an OAuth login attempt provides the same email with a different phone number, findByPhoneNumber will not find the user. The code will then attempt to create a new user with the existing email as loginId, causing a DataIntegrityViolationException.

You should ensure that the field used to look up existing users is consistent with the unique identifier used when creating new users.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

/gemini phoneNumber에 Unique 제약을 걸면 괜찮은거 아니니?

Choose a reason for hiding this comment

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

안녕하세요, wlgns12370님. phoneNumber 필드에 Unique 제약을 추가하는 것은 phoneNumber를 주 식별자로 사용하려는 의도에 부합하는 좋은 단계입니다. 하지만 제 원래 코멘트의 핵심은 OAuthUserPersistenceProcessor에서 기존 사용자를 phoneNumber로 찾으려 하지만, 새로운 사용자를 생성할 때 loginId (현재 info.email()로 설정됨)가 여전히 데이터베이스에 Unique 제약이 있다는 점이었습니다. 만약 phoneNumber에 Unique 제약을 추가하더라도, userRepository.findByPhoneNumber(info.phoneNumber())null을 반환하여 새로운 사용자를 생성할 때 loginIdinfo.email()로 설정된다면, 해당 info.email()이 이미 다른 기존 사용자의 loginId로 존재할 경우 DataIntegrityViolationException이 발생할 수 있습니다. 이는 phoneNumber가 다르더라도 loginId (이메일)가 중복되기 때문입니다. 따라서, 이러한 불일치를 완전히 해결하고 DataIntegrityViolationException을 방지하려면, 사용자 조회 (findByPhoneNumber)와 신규 사용자 생성 시 사용되는 고유 식별자 (loginId) 간의 일관성을 확보해야 합니다. 현재 PR의 변경 사항들 (SignUpAccountStepProcessor에서 existsByPhoneNumber 사용 등)을 보면 phoneNumber를 주 고유 식별자로 사용하려는 의도가 강해 보입니다. 그렇다면 다음을 고려해 주세요: 1. 데이터베이스에 phoneNumber 필드에 Unique 제약을 추가합니다. 2. loginId 필드의 처리 방식을 재검토합니다: * 만약 loginId가 여전히 고유해야 한다면, 신규 사용자 생성 시 loginIdinfo.phoneNumber()에서 파생되도록 설정해야 합니다 (예: user.setLoginId(info.phoneNumber())). * 또는, phoneNumber가 주 고유 키이고 email이 보조 정보라면, loginId (이메일)에 대한 Unique 제약을 제거하거나 다른 방식으로 중복을 허용하는 방법을 고려해야 합니다. 가장 명확한 해결책은 phoneNumber를 새로운 주 고유 식별자로 삼는다면, 신규 사용자 생성 시 loginIdphoneNumber를 기반으로 설정하거나, loginId의 Unique 제약을 제거하는 것입니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🔭 Outside diff range comments (2)
src/main/java/life/mosu/mosuserver/application/oauth/OAuthUserPersistenceProcessor.java (1)

21-45: Normalize and enforce phone-number uniqueness before lookup/save

  • In OAuthUserPersistenceProcessor (lines 21–45), normalize the incoming number with your canonical formatter (e.g.
    String normalizedPhone = PhoneNumberUtil.formatPhoneNumberWithHyphen(info.phoneNumber());
    then use normalizedPhone for both the findByPhoneNumber(...) lookup and when setting
    .phoneNumber(...) on the new entity.
  • Guard against null or blank numbers from the provider. If info.phoneNumber() may be empty, fail fast with a clear error (e.g. ErrorCode.PHONE_NUMBER_REQUIRED) or redirect to your verification step.
  • Add a DB-level uniqueness constraint on phone_number to avoid race-condition duplicates:
    • In UserJpaEntity.java, annotate
    - @Column(name = "phone_number")
    + @Column(name = "phone_number", unique = true)
    • Create a migration to add a unique index on phone_number.
  • Wrap the save in a try/catch for DataIntegrityViolationException (or Spring’s DuplicateKeyException) to map constraint violations to your domain error (e.g. ErrorCode.USER_ALREADY_EXISTS).
src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java (1)

21-25: Enforce DB uniqueness and atomic save for phone numbers and login IDs

To prevent races between existsByPhoneNumber and save, switch to relying on database‐level unique constraints and map any DataIntegrityViolationException to ErrorCode.USER_ALREADY_EXISTS. Also, add a unique constraint on the phone_number column (the login_id column already has one).

Locations to address:

  • SignUpAccountStepProcessor.process: annotate with @Transactional, wrap save in a try‐catch for DataIntegrityViolationException, and remove or keep the pre‐check as a fast-fail hint.
  • UserJpaEntity: add unique = true to the @Column on phoneNumber.
  • DB schema/migration: ensure the phone_number column has a unique constraint.

Suggested changes:

--- a/src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java
+++ b/src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java
@@
-import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.dao.DataIntegrityViolationException;

 @Transactional
 @Override
 public UserJpaEntity process(UserJpaEntity user) {
-    if (userRepository.existsByPhoneNumber(user.getPhoneNumber())) {
-        throw new CustomRuntimeException(ErrorCode.USER_ALREADY_EXISTS);
-    }
-    return userRepository.save(user);
+    try {
+        return userRepository.save(user);
+    } catch (DataIntegrityViolationException e) {
+        // Covers races on unique(phone_number) and login_id
+        throw new CustomRuntimeException(ErrorCode.USER_ALREADY_EXISTS);
+    }
 }
--- a/src/main/java/life/mosu/mosuserver/domain/user/entity/UserJpaEntity.java
+++ b/src/main/java/life/mosu/mosuserver/domain/user/entity/UserJpaEntity.java
@@
-    @Column(name = "phone_number")
+    @Column(name = "phone_number", unique = true)
     private String phoneNumber;
🧹 Nitpick comments (5)
src/main/java/life/mosu/mosuserver/presentation/user/dto/response/IsLoginIdAvailableResponse.java (1)

3-9: Prefer primitive boolean to avoid tri-state and boxing.

This DTO represents a binary flag. Using boolean avoids nulls and unnecessary boxing.

-public record IsLoginIdAvailableResponse(
-        Boolean isLoginIdAvailable
-) {
-    public static IsLoginIdAvailableResponse from(Boolean isLoginIdAvailable) {
-        return new IsLoginIdAvailableResponse(isLoginIdAvailable);
-    }
-}
+public record IsLoginIdAvailableResponse(
+        boolean isLoginIdAvailable
+) {
+    public static IsLoginIdAvailableResponse from(boolean isLoginIdAvailable) {
+        return new IsLoginIdAvailableResponse(isLoginIdAvailable);
+    }
+}
src/main/java/life/mosu/mosuserver/domain/profile/entity/Gender.java (1)

4-6: Good move to domain-specific error; retain invalid value context if possible.

Throwing CustomRuntimeException(ErrorCode.INVALID_GENDER) is better than a generic IAE. If your exception supports details or a message, include the invalid genderName to aid debugging and client feedback; otherwise ensure it’s logged upstream.

Also applies to: 23-23

src/main/java/life/mosu/mosuserver/presentation/user/dto/response/IsPhoneNumberAvailableResponse.java (1)

3-10: Prefer primitive boolean over boxed Boolean for DTO component.

Avoids null tri-state and simplifies usage.

-public record IsPhoneNumberAvailableResponse(
-        Boolean isPhoneNumberAvailable
-) {
-    public static IsPhoneNumberAvailableResponse from(Boolean isPhoneNumberAvailable) {
-        return new IsPhoneNumberAvailableResponse(isPhoneNumberAvailable);
-    }
-}
+public record IsPhoneNumberAvailableResponse(
+        boolean isPhoneNumberAvailable
+) {
+    public static IsPhoneNumberAvailableResponse from(boolean isPhoneNumberAvailable) {
+        return new IsPhoneNumberAvailableResponse(isPhoneNumberAvailable);
+    }
+}
src/main/java/life/mosu/mosuserver/presentation/user/UserController.java (2)

60-69: Path casing consistency.

Other paths use lowercase/hyphen (e.g., /check-id). Consider /check-phone-number for consistency.

-@GetMapping("/check-phoneNumber")
+@GetMapping("/check-phone-number")

Note: update whitelist and any client references if you change it.


64-64: Prefer primitive boolean locally for clarity.

Align with DTO change; avoid boxed type.

-        Boolean isPhoneNumberAvailable = userService.isPhoneNumberAvailable(phoneNumber);
+        boolean isPhoneNumberAvailable = userService.isPhoneNumberAvailable(phoneNumber);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50915bc and 6527121.

📒 Files selected for processing (10)
  • src/main/java/life/mosu/mosuserver/application/auth/processor/SignUpAccountStepProcessor.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/application/oauth/OAuthUserPersistenceProcessor.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/application/user/UserService.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/profile/entity/Gender.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/domain/user/repository/UserJpaRepository.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/global/filter/Whitelist.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/UserController.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/UserControllerDocs.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/dto/response/IsLoginIdAvailableResponse.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/user/dto/response/IsPhoneNumberAvailableResponse.java (1 hunks)
🔇 Additional comments (1)
src/main/java/life/mosu/mosuserver/presentation/user/dto/response/IsLoginIdAvailableResponse.java (1)

1-1: Package move to response makes sense.

Good alignment of DTO location with response semantics.

Comment on lines +55 to +57
public Boolean isPhoneNumberAvailable(String phoneNumber) {
return !userJpaRepository.existsByPhoneNumber(phoneNumber);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use primitive boolean for consistency with isLoginIdAvailable and avoid boxing.

isLoginIdAvailable returns boolean; mirror that here to keep the API consistent and reduce boxing.

-    public Boolean isPhoneNumberAvailable(String phoneNumber) {
-        return !userJpaRepository.existsByPhoneNumber(phoneNumber);
-    }
+    public boolean isPhoneNumberAvailable(final String phoneNumber) {
+        return !userJpaRepository.existsByPhoneNumber(phoneNumber);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public Boolean isPhoneNumberAvailable(String phoneNumber) {
return !userJpaRepository.existsByPhoneNumber(phoneNumber);
}
public boolean isPhoneNumberAvailable(final String phoneNumber) {
return !userJpaRepository.existsByPhoneNumber(phoneNumber);
}
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/application/user/UserService.java around
lines 55 to 57, change the return type of the method isPhoneNumberAvailable from
Boolean to the primitive boolean type to match the return type of
isLoginIdAvailable. This avoids unnecessary boxing and keeps the API consistent.
Update the method signature accordingly without changing the method body.

💡 Verification agent

🧩 Analysis chain

Validate/normalize phone numbers centrally; rely on DB uniqueness at write time.

Even if the controller validates format, consider normalizing to E.164 within the service layer or a dedicated utility to avoid duplicates from formatting. Also ensure a unique constraint on phoneNumber to handle TOCTOU races at signup.

Verify normalization utilities and DB uniqueness:


🏁 Script executed:

#!/bin/bash
rg -n "E164|PhoneNumberUtil|libphonenumber|normalize.*phone" -A 3
rg -n "unique.*phoneNumber|@Column\\(.*unique\\s*=\\s*true.*phoneNumber" -A 3
fd -H "(migration|migrate|db|ddl|flyway|liquibase|schema)" | xargs -I{} rg -n "phoneNumber|UNIQUE|INDEX" {}

Length of output: 1852


Normalize phone numbers to E.164 and enforce uniqueness at the database level

To avoid duplicate accounts caused by different formats and TOCTOU races on signup:

• In src/main/java/life/mosu/mosuserver/application/user/UserService.java (lines 55–57), normalize phoneNumber before checking availability, e.g.

String normalized = PhoneNumberUtil.normalizeToE164(phoneNumber);
return !userJpaRepository.existsByPhoneNumber(normalized);

• Consolidate all formatting in PhoneNumberUtil (e.g. rename/extend formatPhoneNumberWithHyphen to normalizeToE164 using libphonenumber) so every service and controller uses the same logic.

• In your JPA entity (e.g. src/main/java/life/mosu/mosuserver/application/user/UserJpaEntity.java), add

@Column(name = "phone_number", unique = true, nullable = false)
private String phoneNumber;

• Create a database migration (Flyway/Liquibase/DDL) to add a unique index or constraint on the phone_number column to guard against concurrent sign-ups.

🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/application/user/UserService.java lines
55-57, normalize the input phoneNumber to E.164 format using
PhoneNumberUtil.normalizeToE164 before checking its availability with
userJpaRepository.existsByPhoneNumber. Also, update PhoneNumberUtil to include
this normalization method for consistent formatting across the app. In the
UserJpaEntity class, annotate the phoneNumber field with @Column(unique = true,
nullable = false) to enforce uniqueness at the database level. Finally, create a
database migration script to add a unique constraint or index on the
phone_number column to prevent duplicate entries during concurrent sign-ups.

Comment on lines 16 to 17

boolean existsByPhoneNumber(String phoneNumber);
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

LGTM on repository method; ensure DB uniqueness/index for performance.

The derived query is fine. To avoid full scans under load, ensure phoneNumber has a unique index/constraint. Also confirm behavior with soft-deleted users (if any); you may need existsByPhoneNumberAndDeletedFalse-style semantics.

You can verify uniqueness/index and soft-delete usage with:


🏁 Script executed:

#!/bin/bash
# Find phoneNumber field and constraints/usages
rg -n "class UserJpaEntity" -A 200
rg -n "phoneNumber" -A 5
rg -n "@Column\\(.*phoneNumber" -A 3
rg -n "@UniqueConstraint|unique\\s*=\\s*true|UNIQUE" -A 2
# Look for migration files defining a unique index/constraint
fd -H "(migration|migrate|db|ddl|flyway|liquibase|schema)" | xargs -I{} rg -n "phoneNumber|UNIQUE|INDEX" {}

Length of output: 73590


Ensure a DB index/constraint on phone_number
The existsByPhoneNumber lookup will trigger full-table scans under load without an index. We did not find a unique=true on the phone_number column in UserJpaEntity nor any migration adding an index/constraint. Please:

  • Add a database index (or unique constraint if business rules allow) on phone_number.
  • Update your migration (e.g. via Flyway/Liquibase) to create this index.

Note: UserJpaEntity does not implement soft-delete—if you introduce a “deleted” flag later, you’ll need to adjust the repository (e.g. existsByPhoneNumberAndDeletedFalse).

🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/domain/user/repository/UserJpaRepository.java
around lines 16-17, the method existsByPhoneNumber lacks a supporting database
index or unique constraint on the phone_number column, which can cause
full-table scans and degrade performance. To fix this, add a database index or
unique constraint on the phone_number column in the UserJpaEntity mapping if
business rules allow, and update the database migration scripts (e.g., Flyway or
Liquibase) to create this index or constraint. This will optimize lookups by
phone number and prevent performance issues under load.

import life.mosu.mosuserver.global.util.ApiResponseWrapper;
import life.mosu.mosuserver.presentation.user.dto.request.IsLoginIdAvailableResponse;
import life.mosu.mosuserver.presentation.user.dto.response.CustomerKeyResponse;
import life.mosu.mosuserver.presentation.user.dto.response.IsLoginIdAvailableResponse;
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Import relocation looks correct. Consider documenting the new phone number check endpoint here.

If this interface drives OpenAPI docs for User endpoints, add the docs method for /api/v1/user/check-phoneNumber to keep API documentation complete and consistent.

Search whether the new endpoint is documented elsewhere:


🏁 Script executed:

#!/bin/bash
rg -n "check-phoneNumber|IsPhoneNumberAvailableResponse|isPhoneNumberAvailable" -A 5

Length of output: 4523


Add OpenAPI documentation for the new phone-number availability endpoint

The implementation of /api/v1/user/check-phoneNumber is present in UserController.java, but you haven’t exposed it in your OpenAPI docs interface. Please update UserControllerDocs.java to include a docs method for this endpoint, for example:

• File: src/main/java/life/mosu/mosuserver/presentation/user/UserControllerDocs.java
Insert after the isLoginIdAvailable definition:

import life.mosu.mosuserver.presentation.user.dto.response.IsPhoneNumberAvailableResponse;
// …
/**
 * Check whether a given phone number is available for registration.
 *
 * @param phoneNumber the phone number to validate
 * @return HTTP 200 with availability flag wrapped in ApiResponseWrapper
 */
@GetMapping("/check-phoneNumber")
@Operation(summary = "Check phone-number availability")
@ApiResponses({
  @ApiResponse(responseCode = "200", description = "Availability retrieved",
    content = @Content(mediaType = "application/json",
      schema = @Schema(implementation = IsPhoneNumberAvailableResponse.class))),
  // … any error responses
})
ResponseEntity<ApiResponseWrapper<IsPhoneNumberAvailableResponse>> isPhoneNumberAvailable(
    @Parameter(description = "Phone number to check", required = true)
    @PhoneNumberPattern @RequestParam String phoneNumber
);

This will keep your OpenAPI docs in sync with the new endpoint.

🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/presentation/user/UserControllerDocs.java
at line 13, add OpenAPI documentation for the new /api/v1/user/check-phoneNumber
endpoint by defining a method similar to isLoginIdAvailable. Import
IsPhoneNumberAvailableResponse, then create a method annotated with
@GetMapping("/check-phoneNumber"), @Operation, and @ApiResponses that returns
ResponseEntity<ApiResponseWrapper<IsPhoneNumberAvailableResponse>>. Include a
@Parameter for the phoneNumber request parameter with validation annotation
@PhoneNumberPattern. This will expose the new endpoint in the OpenAPI docs.

@mosu-dev mosu-dev deleted a comment from gemini-code-assist bot Aug 10, 2025
@wlgns12370 wlgns12370 merged commit 4cba953 into develop Aug 10, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[🛠 기능 구현] 등록된 전화번호 확인 api 구현

1 participant