Skip to content

[DEV-265/BE] feat: 회사를 검색하는 API 개발#393

Open
lja3723 wants to merge 12 commits intodevfrom
DEV-265/feat/회사를-검색하는-API-개발

Hidden character warning

The head ref may contain hidden characters: "DEV-265/feat/\ud68c\uc0ac\ub97c-\uac80\uc0c9\ud558\ub294-API-\uac1c\ubc1c"
Open

[DEV-265/BE] feat: 회사를 검색하는 API 개발#393
lja3723 wants to merge 12 commits intodevfrom
DEV-265/feat/회사를-검색하는-API-개발

Conversation

@lja3723
Copy link
Collaborator

@lja3723 lja3723 commented Feb 15, 2026

관련 이슈

close #390

작업한 내용

  • 회사를 검색하는 API를 개발하였습니다.
    • 한글을 자소단위로 분리하는 유틸 추가
      • hangulUtil.decompose('두찜닭스쉼') -> "ㄷㅜㅉㅣㅁㄷㅏㄺㅅㅡㅅㅟㅁ"
    • 회사에 자소단위로 분리된 한글을 저장하는 필드 추가 (searchName)

PR 리뷰시 참고할 사항

추후 ㅟ, ㄺ 같은 것도 분리 고려

@lja3723 lja3723 self-assigned this Feb 15, 2026
@vercel
Copy link

vercel bot commented Feb 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
web-team4-refit Ignored Ignored Preview Feb 15, 2026 2:51pm

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @lja3723, 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!

이 PR은 회사 검색 기능을 구현하기 위해 백엔드에 새로운 API 엔드포인트를 추가하고 관련 로직을 개선합니다. 특히, 한글 검색의 정확도를 높이기 위해 한글을 초성, 중성, 종성 단위로 분리하는 유틸리티를 도입하고, 이 분리된 문자열을 저장하는 searchName 필드를 Company 엔티티에 추가하여 검색에 활용합니다. 이를 통해 사용자가 입력하는 검색어와 관계없이 보다 유연하고 정확한 회사 검색이 가능해집니다.

Highlights

  • 회사 검색 API 개발: 회사를 검색하는 새로운 API 엔드포인트가 추가되었습니다.
  • 한글 자소 분리 유틸리티 추가: 한글을 초성, 중성, 종성 단위로 분리하는 HangulUtil이 추가되어 검색 정확도를 높입니다.
  • Company 모델에 searchName 필드 추가: Company 엔티티에 자소 단위로 분리된 한글 이름을 저장하는 searchName 필드가 추가되었습니다.
Changelog
  • backend/src/main/java/com/shyashyashya/refit/domain/company/api/CompanyController.java
    • 회사 검색을 위한 GET /company 엔드포인트를 추가했습니다.
  • backend/src/main/java/com/shyashyashya/refit/domain/company/api/response/CompanyResponse.java
    • 회사 검색 결과 응답을 위한 CompanyResponse 레코드를 정의했습니다.
  • backend/src/main/java/com/shyashyashya/refit/domain/company/model/Company.java
    • searchName 필드를 추가했습니다.
    • create 팩토리 메서드를 업데이트하여 searchName을 받도록 변경했습니다.
    • allowSearch 메서드를 추가했습니다.
  • backend/src/main/java/com/shyashyashya/refit/domain/company/repository/CompanyRepository.java
    • 검색 쿼리를 사용하여 회사 목록을 페이지네이션하여 조회하는 findAllBySearchQuery 메서드를 추가했습니다.
  • backend/src/main/java/com/shyashyashya/refit/domain/company/service/CompanyService.java
    • HangulUtil을 사용하여 검색 쿼리를 자소 분리하고, CompanyRepository를 통해 회사 목록을 조회하는 findCompanies 메서드를 구현했습니다.
  • backend/src/main/java/com/shyashyashya/refit/domain/interview/service/InterviewService.java
    • HangulUtil을 주입했습니다.
    • 새로운 회사를 생성할 때 companyName을 자소 분리하여 searchName으로 저장하도록 로직을 수정했습니다.
  • backend/src/main/java/com/shyashyashya/refit/global/util/HangulUtil.java
    • 한글 완성형 음절을 초성, 중성, 종성으로 분리하는 decompose 메서드를 포함하는 유틸리티 클래스를 추가했습니다.
  • backend/src/test/java/com/shyashyashya/refit/core/IntegrationTest.java
    • HangulUtil을 주입했습니다.
    • 테스트 셋업에서 createAndSaveCompany 메서드가 HangulUtil을 사용하도록 업데이트했습니다.
  • backend/src/test/java/com/shyashyashya/refit/fixture/CompanyFixture.java
    • TEST_COMPANY 객체 생성 시 HangulUtil을 사용하여 searchName을 설정하도록 수정했습니다.
  • backend/src/test/java/com/shyashyashya/refit/global/util/HangulUtilTest.java
    • HangulUtil의 한글 자소 분리 기능에 대한 다양한 단위 테스트 케이스를 추가했습니다.
Activity
  • 이 PR에는 현재까지 별다른 리뷰 활동이나 코멘트가 없습니다.
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 by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

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 pull request 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. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

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
Contributor

@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

안녕하세요. 회사 검색 API 개발 PR 리뷰입니다. 한글 자소 분리를 이용한 검색 기능 추가가 인상적입니다. HangulUtil에 대한 테스트 케이스를 꼼꼼하게 작성해주셔서 코드의 신뢰성이 높아졌습니다. 전반적으로 코드가 깔끔하게 작성되었으나, 엔티티 팩토리 메서드에서 비즈니스 로직 값 처리와 관련된 잠재적인 개선 사항을 발견하여 코멘트를 남겼습니다. 확인 부탁드립니다.

public class CompanyFixture {

public static final Company TEST_COMPANY = Company.create("test company", "test.com/logo.png", true);
public static final Company TEST_COMPANY = Company.create("test company", new HangulUtil().decompose("test company"), "test.com/logo.png");
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The change in the Company.create factory method's signature has implicitly set isSearchAllowed to false for TEST_COMPANY. This introduces a hardcoded business logic value within the factory method, which can lead to unexpected test failures if other tests rely on TEST_COMPANY being searchable (isSearchAllowed=true).

According to the rule 'Avoid hardcoding business logic values in entity factory methods. Pass them as parameters to improve clarity and flexibility, leaving business rule enforcement to service layers.', it is recommended to modify the Company.create factory method to explicitly accept isSearchAllowed as a parameter. This approach improves clarity and flexibility, allowing the desired state to be set directly during object creation, rather than relying on post-creation calls like allowSearch() or implicit defaults.

References
  1. Avoid hardcoding business logic values in entity factory methods. Pass them as parameters to improve clarity and flexibility, leaving business rule enforcement to service layers.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a company search API that enables users to search for companies using Korean text with automatic jamo (character component) decomposition for flexible search matching. The implementation adds a HangulUtil utility class to decompose Korean characters into their constituent jamo components (초성, 중성, 종성), stores this decomposed form in a new searchName field, and provides a paginated search endpoint.

Changes:

  • Added HangulUtil component for Korean character decomposition with comprehensive test coverage
  • Extended Company entity with searchName field to store decomposed Korean text for search optimization
  • Implemented company search API endpoint with pagination support and query filtering based on decomposed text

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
backend/src/main/java/com/shyashyashya/refit/global/util/HangulUtil.java New utility class for decomposing Korean characters into jamo components (초성, 중성, 종성)
backend/src/test/java/com/shyashyashya/refit/global/util/HangulUtilTest.java Comprehensive test suite for HangulUtil covering edge cases and various Korean character combinations
backend/src/main/java/com/shyashyashya/refit/domain/company/model/Company.java Added searchName field and allowSearch() method; updated factory method signature
backend/src/main/java/com/shyashyashya/refit/domain/company/repository/CompanyRepository.java Added findAllBySearchQuery method with JPQL query for prefix-based search on decomposed text
backend/src/main/java/com/shyashyashya/refit/domain/company/service/CompanyService.java New service implementing company search logic with null handling and query decomposition
backend/src/main/java/com/shyashyashya/refit/domain/company/api/CompanyController.java New REST controller exposing GET /company endpoint for paginated company search
backend/src/main/java/com/shyashyashya/refit/domain/company/api/response/CompanyResponse.java New response DTO for company search results
backend/src/main/java/com/shyashyashya/refit/domain/interview/service/InterviewService.java Updated company creation to include searchName decomposition
backend/src/test/java/com/shyashyashya/refit/fixture/CompanyFixture.java Updated fixture to include searchName field
backend/src/test/java/com/shyashyashya/refit/core/IntegrationTest.java Added helper methods for creating companies with searchName and isSearchAllowed flag

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +27 to +28
@RequestParam(required = false) String q, @ParameterObject Pageable pageable) {
var response = companyService.findCompanies(q, pageable);
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The query parameter name 'q' is not descriptive and could be confusing for API consumers. Consider using a more meaningful parameter name like 'query' or 'searchQuery' to improve API clarity. While this is a common shorthand in some APIs, the codebase doesn't show a consistent pattern for abbreviated parameter names in similar search endpoints.

Suggested change
@RequestParam(required = false) String q, @ParameterObject Pageable pageable) {
var response = companyService.findCompanies(q, pageable);
@RequestParam(name = "query", required = false) String query, @ParameterObject Pageable pageable) {
var response = companyService.findCompanies(query, pageable);

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +29
@Column(name = "search_name", columnDefinition = "varchar(80)")
private String searchName;
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The searchName field has a maximum length of 80 characters (varchar(80)), but the name field is only 20 characters (varchar(20)). Since Korean characters decompose into 2-3 jamo characters each, a 20-character Korean company name could expand to approximately 40-60 characters when decomposed. However, the 80-character limit should be sufficient. Consider adding validation to ensure that the decomposed searchName doesn't exceed this limit, or documenting why 80 characters is sufficient for the expected use case.

Copilot uses AI. Check for mistakes.
Comment on lines 18 to 32
@RestController
@RequiredArgsConstructor
@RequestMapping("/company")
public class CompanyController {

private final CompanyService companyService;

@GetMapping
public ResponseEntity<ApiResponse<Page<CompanyResponse>>> findCompanies(
@RequestParam(required = false) String q, @ParameterObject Pageable pageable) {
var response = companyService.findCompanies(q, pageable);
var body = ApiResponse.success(COMMON200, response);
return ResponseEntity.ok(body);
}
}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

There is a significant discrepancy between the PR title/description and the mentioned issue #265. The PR is about implementing a company search API (DEV-265/BE), but the mentioned issue #265 is about "스크랩 폴더 목록 조회하는 API 구현" (DEV-191/BE - retrieving scrap folder lists with QnA set inclusion status). This appears to be a mismatch in the issue reference. Please verify that the correct issue is linked to this PR.

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +21
public Page<CompanyResponse> findCompanies(String query, Pageable pageable) {
query = (query == null || query.isBlank()) ? "" : hangulUtil.decompose(query);
return companyRepository.findAllBySearchQuery(query, pageable);
}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The new company search API endpoint lacks integration test coverage. The codebase has extensive integration tests for similar GET endpoints (e.g., InterviewIntegrationTest, QnaSetIntegrationTest). Consider adding integration tests to verify:

  1. Successful company search with a valid query
  2. Search with empty/null query returns all allowed companies
  3. Search correctly filters by searchName prefix
  4. Search respects isSearchAllowed flag (only returns companies where isSearchAllowed=true)
  5. Pagination works correctly
  6. Hangul decomposition is properly applied to the search query

Copilot uses AI. Check for mistakes.
public class CompanyFixture {

public static final Company TEST_COMPANY = Company.create("test company", "test.com/logo.png", true);
public static final Company TEST_COMPANY = Company.create("test company", new HangulUtil().decompose("test company"), "test.com/logo.png");
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The CompanyFixture creates a new HangulUtil instance directly instead of using dependency injection. This is inconsistent with how HangulUtil is used elsewhere in the codebase (as a Spring-managed @component). While this works for test fixtures, it's better practice to either:

  1. Make HangulUtil methods static if they don't require any dependencies (since it's a pure utility), or
  2. Pass HangulUtil as a parameter to a factory method in the fixture

Consider refactoring HangulUtil to have static methods since it has no dependencies and is purely computational, which would make it easier to use in static fixture contexts.

Suggested change
public static final Company TEST_COMPANY = Company.create("test company", new HangulUtil().decompose("test company"), "test.com/logo.png");
public static Company createTestCompany(HangulUtil hangulUtil) {
return Company.create("test company", hangulUtil.decompose("test company"), "test.com/logo.png");
}

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +29
@Column(name = "search_name", columnDefinition = "varchar(80)")
private String searchName;
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The searchName field is used in a LIKE query with a prefix pattern (CONCAT(:query, '%')) but lacks a database index. For optimal search performance, especially as the number of companies grows, consider adding an index on the searchName column. This would significantly improve query performance for prefix searches. You can add this using JPA's @table annotation with @Index, or through a database migration script.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,3 @@
package com.shyashyashya.refit.domain.company.api.response;

public record CompanyResponse(Long companyId, String companyName) {}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The CompanyResponse record doesn't include the logoUrl field, while the existing CompanyDto does include it (companyLogoUrl). Consider whether the company logo URL should be included in the search results, as it would likely be useful for displaying company logos in the UI when showing search results. If this omission is intentional, it's acceptable, but it's worth confirming the expected use case.

Suggested change
public record CompanyResponse(Long companyId, String companyName) {}
public record CompanyResponse(Long companyId, String companyName, String companyLogoUrl) {}

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +25
@RestController
@RequiredArgsConstructor
@RequestMapping("/company")
public class CompanyController {

private final CompanyService companyService;

@GetMapping
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The CompanyController is missing API documentation annotations. According to the codebase conventions, all controllers should have @tag annotation at the class level and @operation annotation at the method level to provide proper Swagger/OpenAPI documentation. Please add:

  • @tag annotation with name and description at the class level
  • @operation annotation with a summary (and optionally description) at the method level

Example based on similar controllers in the codebase:

@Tag(name = "Company API", description = "회사 관련 API 입니다.")

and

@Operation(summary = "회사 목록을 검색합니다.")

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@kckc0608 kckc0608 left a comment

Choose a reason for hiding this comment

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

LGTM 수고하셨습니다!

Comment on lines +26 to +27
public ResponseEntity<ApiResponse<Page<CompanyResponse>>> findCompanies(
@RequestParam(required = false) String q, @ParameterObject Pageable pageable) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

변수명 q 보다 구체적으로 적어주시면 가독성이 더 좋을 것 같습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

api 사용시 /company?q=캌 처럼 검색되는 걸 원하긴 했는데, 그러면 그냥 어노테이션에만 q로 명시하고 변수명은 구체적으로 써도 괜찮나요?

Comment on lines +14 to +25
@Query(value = """
SELECT new com.shyashyashya.refit.domain.company.api.response.CompanyResponse(c.id, c.name)
FROM Company c
WHERE LOWER(c.searchName) LIKE LOWER(CONCAT(:query, '%'))
AND c.isSearchAllowed = TRUE
""", countQuery = """
SELECT COUNT(c)
FROM Company c
WHERE LOWER(c.searchName) LIKE LOWER(CONCAT(:query, '%'))
AND c.isSearchAllowed = TRUE
""")
Page<CompanyResponse> findAllBySearchQuery(String query, Pageable pageable);
Copy link
Collaborator

Choose a reason for hiding this comment

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

쿼리 dsl 로 프로젝션하면 좋을 것 같습니다!

Copy link
Collaborator

Choose a reason for hiding this comment

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

++ 만약에 후속에서 하시겠다고 하면 투두 주석 남겨두면 좋을 것 같아요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

후속에서 하겠습니다!

Copy link
Collaborator

Choose a reason for hiding this comment

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

자세한 테스트코드 좋은데요~
혹시 한글 + 공백 + 한글 과 같은 조합, 공백이 포함된 한글 + 숫자 조합, 한글 + 영어 + 숫자 조합도 테스트 하면 어떨까요? 회사 이름에 공백도 충분히 들어갈 수 있을 것 같아서요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

좋은 제안 감사합니다. 반영하겠습니다

Comment on lines -37 to +44
public static Company create(String name, String logoUrl, boolean isSearchAllowed) {
public static Company create(String name, String searchName, String logoUrl) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

검색용 이름이라고 하니까 비즈니스 로직을 도메인이 알고 있는 느낌이 드는데, 혹시 decomposedName 과 같은 변수명은 어떻게 생각하시나요?

Comment on lines +19 to +20
query = (query == null || query.isBlank()) ? "" : hangulUtil.decompose(query);
return companyRepository.findAllBySearchQuery(query, pageable);
Copy link
Collaborator

Choose a reason for hiding this comment

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

null, blank 처리를 decompose 내부에서 빈문자 반환 처리하는 건 어떤 것 같으세요?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DEV-265/] feat: 회사를 검색하는 API 개발

2 participants