Skip to content

Commit 6686652

Browse files
authored
[feat/OPS-392] 사용자 검색 기능 추가 (#137)
* feat/OPS-392: 검색 기능 추가 & ElasticSearch 구현 & 일부 도메인 오류 수정. * feat/OPS-392: CI 워크플로우에 ElasticSearch 서비스 컨테이너 생성 로직 추가. * feat/OPS-392: CI 워크플로우에 ElasticSearch 서비스 컨테이너 생성 로직 추가 #2 * feat/OPS-392: 추가 도메인 오류 수정. * feat/OPS-392: Docker-compose에 elastic-search 서비스 항목 추가.
1 parent 76d795e commit 6686652

File tree

19 files changed

+331
-7
lines changed

19 files changed

+331
-7
lines changed

.github/workflows/prod-server.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ jobs:
109109
-e JWT_ACCESS_TOKEN_VALIDITY="${{secrets.JWT_ACCESS_TOKEN_VALIDITY}}" \
110110
-e JWT_REFRESH_TOKEN_VALIDITY="${{secrets.JWT_REFRESH_TOKEN_VALIDITY}}" \
111111
-e FRONT_REDIRECT_DOMAIN="${{secrets.FRONT_REDIRECT_DOMAIN}}" \
112+
-e ELASTIC_HOST="${{secrets.PROD_ELASTIC_HOST}}" \
112113
ghcr.io/${{ github.repository }}/zoopzoop:latest
113114
114115

.github/workflows/test-server-cd.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ jobs:
8686
-e JWT_ACCESS_TOKEN_VALIDITY="${{secrets.JWT_ACCESS_TOKEN_VALIDITY}}" \
8787
-e JWT_REFRESH_TOKEN_VALIDITY="${{secrets.JWT_REFRESH_TOKEN_VALIDITY}}" \
8888
-e FRONT_REDIRECT_DOMAIN="${{secrets.FRONT_REDIRECT_DOMAIN}}" \
89+
-e ELASTIC_HOST="${{secrets.TEST_ELASTIC_HOST}}" \
8990
ghcr.io/${{ github.repository }}/zoopzoop:latest
9091
9192
# 헬스체크

.github/workflows/test-server-ci.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ jobs:
2323
env:
2424
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
2525

26-
27-
# CI 작업이 실행되는 동안 RabbitMQ 서비스 컨테이너를 함께 실행
2826
services:
27+
# CI 작업이 실행되는 동안 RabbitMQ 서비스 컨테이너를 함께 실행
2928
rabbitmq:
3029
image: rabbitmq:3-management
3130
ports:
@@ -37,6 +36,16 @@ jobs:
3736
--health-timeout 5s
3837
--health-retries 5
3938
39+
# CI 작업이 실행되는 동안 ElasticSearch 서비스 컨테이너 함께 실행
40+
elasticsearch:
41+
image: docker.elastic.co/elasticsearch/elasticsearch:8.18.5
42+
ports:
43+
- 9200:9200
44+
options: >-
45+
--env discovery.type=single-node
46+
--env xpack.security.enabled=false
47+
--env ES_JAVA_OPTS="-Xms512m -Xmx512m"
48+
4049
steps:
4150
# 1. 소스 코드 체크아웃
4251
- name: Checkout source code
@@ -73,6 +82,8 @@ jobs:
7382
SPRING_RABBITMQ_HOST: localhost
7483
SPRING_RABBITMQ_USERNAME: guest
7584
SPRING_RABBITMQ_PASSWORD: guest
85+
SPRING_DATA_ELASTICSEARCH_HOST: localhost
86+
SPRING_DATA_ELASTICSEARCH_PORT: 9200
7687
KAKAO_CLIENT_ID: ${{ secrets.OAUTH_KAKAO_CLIENT_ID }}
7788
GOOGLE_CLIENT_ID: ${{ secrets.OAUTH_GOOGLE_CLIENT_ID }}
7889
GOOGLE_CLIENT_SECRET: ${{ secrets.OAUTH_GOOGLE_CLIENT_SECRET }}

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ dependencies {
125125

126126
// Awaitility (비동기 테스트 지원)
127127
testImplementation 'org.awaitility:awaitility:4.2.0'
128+
129+
// Elastic Search
130+
implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
128131
}
129132

130133
dependencyManagement {

docker-compose.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,19 @@ services:
1313
volumes:
1414
- rabbitmq-data:/var/lib/rabbitmq
1515

16+
elasticsearch:
17+
image: docker.elastic.co/elasticsearch/elasticsearch:8.18.5
18+
container_name: local-elasticsearch
19+
ports:
20+
- "9200:9200"
21+
environment:
22+
discovery.type: single-node
23+
xpack.security.enabled: "false"
24+
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
25+
restart: unless-stopped
26+
volumes:
27+
- elasticsearch-data:/usr/share/elasticsearch/data
28+
1629
volumes:
1730
rabbitmq-data:
31+
elasticsearch-data:

src/main/java/org/tuna/zoopzoop/backend/domain/auth/handler/OAuth2FailureHandler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,16 @@ public void onAuthenticationFailure(HttpServletRequest request,
2929
String source = request.getParameter("source");
3030

3131
if("extension".equals(source)){
32-
String redirectUrl = redirect_domain + "/extension/callback "
32+
String redirectUrl =
33+
"https://" + redirect_domain + "/extension/callback "
3334
+ "?success=false"
3435
+ "&error=" + URLEncoder.encode(exception.getMessage(), "UTF-8");
3536
response.sendRedirect(redirectUrl);
3637
return;
3738
}
3839

3940
String redirectUrl =
40-
redirect_domain + "/auth/callback"
41+
"https://" + redirect_domain + "/auth/callback"
4142
+ "?success=false"
4243
+ "&error=" + URLEncoder.encode(exception.getMessage(), "UTF-8");
4344

src/main/java/org/tuna/zoopzoop/backend/domain/auth/handler/OAuth2SuccessHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
9191
// 확장 프로그램에서 로그인 했을 경우.
9292
if ("extension".equals(source)) {
9393
authResult.put(customState, accessToken, sessionId);
94-
response.sendRedirect(redirect_domain + "/extension/success");
94+
response.sendRedirect("https://" + redirect_domain + "/extension/success");
9595
response.flushBuffer();
9696
return;
9797
}
@@ -132,7 +132,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
132132

133133
// 로그인 성공 후 리다이렉트.
134134
// 배포 시에 프론트엔드와 조율이 필요한 부분일 듯 함.
135-
response.sendRedirect(redirect_domain + "/api/auth/callback");
135+
response.sendRedirect("https://" + redirect_domain + "/api/auth/callback");
136136
}
137137
// 보안을 좀 더 강화하고자 한다면 CSRF 토큰 같은 걸 생각해볼 수 있겠으나,
138138
// 일단은 구현하지 않음.(개발 과정 중에 번거로워질 수 있을 듯 함.)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.tuna.zoopzoop.backend.domain.member.controller;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.springframework.http.HttpStatus;
5+
import org.springframework.http.ResponseEntity;
6+
import org.springframework.web.bind.annotation.GetMapping;
7+
import org.springframework.web.bind.annotation.RequestMapping;
8+
import org.springframework.web.bind.annotation.RequestParam;
9+
import org.springframework.web.bind.annotation.RestController;
10+
import org.tuna.zoopzoop.backend.domain.member.dto.res.ResBodyForSearchMember;
11+
import org.tuna.zoopzoop.backend.domain.member.entity.MemberDocument;
12+
import org.tuna.zoopzoop.backend.domain.member.service.MemberSearchService;
13+
import org.tuna.zoopzoop.backend.domain.member.service.MemberService;
14+
import org.tuna.zoopzoop.backend.global.rsData.RsData;
15+
16+
import java.util.List;
17+
18+
@RestController
19+
@RequiredArgsConstructor
20+
@RequestMapping("api/v1/member")
21+
public class ApiV1MemberSearchController {
22+
private final MemberSearchService memberSearchService;
23+
private final MemberService memberService;
24+
25+
@GetMapping("/search")
26+
public ResponseEntity<RsData<List<ResBodyForSearchMember>>> searchMembers(
27+
@RequestParam String keyword
28+
) {
29+
List<MemberDocument> memberDocuments = memberSearchService.searchByName(keyword);
30+
List<ResBodyForSearchMember> memberDtos = memberDocuments.stream()
31+
.map(ResBodyForSearchMember::new)
32+
.toList();
33+
return ResponseEntity
34+
.status(HttpStatus.OK)
35+
.body(
36+
new RsData<>(
37+
"200",
38+
"검색 조건에 맞는 사용자들을 조회 했습니다.",
39+
memberDtos
40+
)
41+
);
42+
}
43+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.tuna.zoopzoop.backend.domain.member.dto.res;
2+
3+
import org.tuna.zoopzoop.backend.domain.member.entity.MemberDocument;
4+
5+
public record ResBodyForSearchMember(
6+
int id,
7+
String name
8+
) {
9+
public ResBodyForSearchMember(int id, String name) {
10+
this.id = id;
11+
this.name = name;
12+
}
13+
public ResBodyForSearchMember(MemberDocument memberDocument){
14+
this(memberDocument.getId(), memberDocument.getName());
15+
}
16+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.tuna.zoopzoop.backend.domain.member.entity;
2+
3+
import jakarta.persistence.Id;
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
import org.springframework.data.elasticsearch.annotations.Document;
7+
import org.springframework.data.elasticsearch.annotations.Field;
8+
import org.springframework.data.elasticsearch.annotations.FieldType;
9+
import org.springframework.data.elasticsearch.annotations.Setting;
10+
11+
@Document(indexName = "member")
12+
@Setting(settingPath = "ngram.json")
13+
@Getter
14+
@Setter
15+
public class MemberDocument {
16+
@Id
17+
private int id;
18+
19+
@Field(type = FieldType.Text, analyzer = "ngram_analyzer", searchAnalyzer = "standard")
20+
private String name;
21+
}

0 commit comments

Comments
 (0)