Skip to content
Merged
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 @@ -89,9 +89,9 @@ Optional<ExamTicketInfoProjection> findExamTicketInfoProjectionById(
FROM ExamApplicationJpaEntity ea
JOIN PaymentJpaEntity p ON p.examApplicationId = ea.id
LEFT JOIN RefundJpaEntity r ON r.examApplicationId = ea.id
WHERE p.paymentStatus = 'DONE'
AND (r IS NULL OR r.refundStatus = 'DONE')
WHERE (p.paymentStatus = 'DONE' OR r.refundStatus = 'DONE')

Choose a reason for hiding this comment

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

medium

현재 WHERE 절의 (p.paymentStatus = 'DONE' OR r.refundStatus = 'DONE') 조건은 논리적으로 p.paymentStatus = 'DONE'과 동일한 결과를 반환할 것으로 보입니다. 환불이 완료(r.refundStatus = 'DONE')되려면 반드시 결제가 먼저 완료(p.paymentStatus = 'DONE')되어야 하기 때문입니다.

OR를 사용한 현재 조건은 혼란을 줄 수 있으며, 데이터 무결성이 깨진 예외적인 경우(결제 미완료 상태에서 환불 완료)에 의도치 않은 동작을 유발할 수 있습니다. 또한, OR 조건은 데이터베이스 쿼리 성능에 미미한 영향을 줄 수도 있습니다.

코드를 더 명확하고 견고하게 만들기 위해 조건을 p.paymentStatus = 'DONE'으로 단순화하는 것을 제안합니다. 이렇게 하면 결제가 완료된 모든 신청을 조회하며, CASE 문을 통해 환불 상태를 정확히 표시할 수 있습니다.

Suggested change
WHERE (p.paymentStatus = 'DONE' OR r.refundStatus = 'DONE')
WHERE p.paymentStatus = 'DONE'

AND ea.applicationId IN :applicationIds
AND ea.deleted = false
""")
List<ExamApplicationWithStatus> findByApplicationIdIn(List<Long> applicationIds);

Expand Down
76 changes: 45 additions & 31 deletions src/main/java/life/mosu/mosuserver/global/util/IpUtil.java
Original file line number Diff line number Diff line change
@@ -1,48 +1,62 @@
package life.mosu.mosuserver.global.util;

import jakarta.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Arrays;
import java.util.stream.Stream;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.experimental.UtilityClass;

@UtilityClass
public class IpUtil {

public static String getClientIp(HttpServletRequest request) throws UnknownHostException {
String ip = request.getHeader("X-Forwarded-For");
private static final List<String> IP_HEADERS = Arrays.asList(
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_CLIENT_IP",
"HTTP_X_FORWARDED_FOR",
"X-Real-IP"
);

public static String getClientIp(HttpServletRequest request) {
String ip = IP_HEADERS.stream()
.map(request::getHeader)
.filter(IpUtil::isValidIp)
.findFirst()
.orElse(request.getRemoteAddr());

String proxiedIp = resolveProxiedIp(ip);
return Loopback.resolve(proxiedIp);
}

private static String resolveProxiedIp(String ip) {
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
return ip.split(",")[0].trim();
}
return ip;
}

if (isInvalidIp(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (isInvalidIp(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (isInvalidIp(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (isInvalidIp(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (isInvalidIp(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (isInvalidIp(ip)) {
ip = request.getRemoteAddr();
}
private static boolean isValidIp(String ip) {
return ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip);
}

if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {
InetAddress inetAddress = InetAddress.getLocalHost();
ip = inetAddress.getHostName() + "/" + inetAddress.getHostAddress();
}
@RequiredArgsConstructor
@Getter
private enum Loopback {
V4("127.0.0.1"),
V6("0:0:0:0:0:0:0:1");

return ip;
}
private final String address;

private static boolean isInvalidIp(String ip) {
return ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip);
public static boolean isLoopback(String ip) {
return Arrays.stream(Loopback.values())
.anyMatch(loopback -> loopback.getAddress().equals(ip));
}
public static String resolve(String ip) {
return isLoopback(ip) ? "localhost" : ip;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public record InquiryCreateRequest(
@Size(max = 1000, message = "본문은 최대 1000자까지 입력 가능합니다.")
@Schema(description = "문의 내용", example = "포인트는 어떻게 사용하나요?")
@NotNull String content,

@Size(max = 3, message = "첨부파일은 최대 3개까지 첨부할 수 있습니다.")
List<FileRequest> attachments
) {

Expand Down