728x90

접속로그를 동적 쿼리로 검색하는 걸 구현하는 예제를 적어둔다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
@Service
@Transactional
@Log4j2
@RequiredArgsConstructor // 생성자 자동 주입
public class AccessLogSearchImpl implements AccessLogSearch {
    private final JPAQueryFactory queryFactory;
    private final ErrorCodeRepository errorCodeRepository;
 
    @Override
    public Page<AccessLogDTO> search1(PageRequestDTO pageRequestDTO) {
        QAccessLog accessLog = QAccessLog.accessLog;
 
        BooleanBuilder builder = new BooleanBuilder();
 
        String where = pageRequestDTO.getFilter().getWhere();
        String keyword = pageRequestDTO.getFilter().getKeyword();
 
        log.info("검색 필터 where = {}, keyword = {}", where, keyword);
 
        if (keyword != null && !keyword.trim().isEmpty()) {
            switch (where) {
                case "userID":
                    builder.and(accessLog.userid.containsIgnoreCase(keyword));
                    break;
                case "userNM":
                    builder.and(accessLog.userNM.containsIgnoreCase(keyword));
                    break;
                case "ipaddr":
                    builder.and(accessLog.ipaddr.containsIgnoreCase(keyword));
                    break;
                case "route":
                    builder.and(accessLog.route.stringValue().containsIgnoreCase(keyword));
                    break;
                case "errorCode":
                    builder.and(accessLog.errCode.stringValue().containsIgnoreCase(keyword));
                    break;
                case "accessDate":
                    if (keyword.contains("/")) {
                        String[] parts = keyword.split("/");
                        if (parts.length == 2) {
                            String from = parts[0].trim();
                            String to = parts[1].trim();
 
                            log.info("accessDate 조건: from = {}, to = {}", from, to);
 
                            if (from.length() == 8 && to.length() == 8) {
                                // 날짜 순서 보정
                                if (from.compareTo(to) > 0) {
                                    String temp = from;
                                    from = to;
                                    to = temp;
                                }
                                log.info("accessDate 조건: from = {}, to = {}", from, to);
                                builder.and(accessLog.date.between(from, to));
                            }
                        }
                    } else {
                        builder.and(accessLog.date.startsWith(keyword));
                    }
                    break;
                default:
                    // where가 명시되지 않은 컬럼이라면, 문자열 컬럼으로 간주하고 LIKE 처리
                    // 존재하지 않는 필드면 builder에 아무 조건도 추가하지 않음 → 결과 없음
                    try {
                        PathBuilder<AccessLog> entityPath = new PathBuilder<>(AccessLog.class"accessLog");
                        // 유효한 필드인지 체크
                        Field field = AccessLog.class.getDeclaredField(where);
                        if (field.getType().equals(String.class)) {
                            builder.and(entityPath.getString(where).containsIgnoreCase(keyword));
                        } else {
                            log.warn(" '{}' 필드는 문자열(String)이 아닙니다. 검색 제외됨", where);
                        }
                    } catch (NoSuchFieldException e) {
                        log.warn(" 존재하지 않는 where 필드명: '{}'. 조건 제외 → 결과 없음 처리", where);
                        // builder에 조건 추가 안 함 → 결과 없음 유도
                    }
 
            }
        }
 
        Pageable pageable = PageRequest.of(
                pageRequestDTO.getPage() - 1,
                pageRequestDTO.getSize(),
                Sort.by("uid").descending()
        );
 
        JPAQuery<AccessLog> query = queryFactory
                .selectFrom(accessLog)
                .where(builder)
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .orderBy(accessLog.uid.desc());
 
        List<AccessLog> resultList = query.fetch();
 
        long totalCount = queryFactory
                .select(accessLog.count())
                .from(accessLog)
                .where(builder)
                .fetchOne();
 
        List<AccessLogDTO> dtoList = IntStream.range(0, resultList.size())
                .mapToObj(i -> {
                    AccessLog entity = resultList.get(i);
                    int no = (int) (totalCount - ((pageable.getPageNumber()) * pageable.getPageSize()) - i);
                    return toDTO(entity, no);
                })
                .collect(Collectors.toList());
 
        return new PageImpl<>(dtoList, pageable, totalCount);
    }
 
    private AccessLogDTO toDTO(AccessLog entity, int no) {
        String errorMessage = errorCodeRepository
                .findMessageByCode(entity.getErrCode())
                .orElse(String.valueOf(entity.getErrCode()));
 
        return AccessLogDTO.builder()
                .no(no)  // 추가
                .uid(entity.getUid())
                .ipaddr(MaskingUtil.ipAddressMasking(entity.getIpaddr()))
                .date(InputSanitizer.displayDate(entity.getDate()))
                .time(entity.getTime())
                .OS(entity.getOs())
                .browser(entity.getBrowser())
                .userid(MaskingUtil.idMasking(entity.getUserid()))
                .userNM(MaskingUtil.letterMasking(entity.getUserNM()))
                .success(entity.getSuccess())
                .route(entity.getRoute())
                .errCode(entity.getErrCode())
                .errorMessage(errorMessage)
                .build();
    }
 
}
 

 

거의 동일한 코드를 최적화한 코드는 이 다음 게시글에 적어둘 것이다.

 

728x90
블로그 이미지

Link2Me

,