초성검색 적용전에 암호화된 성명 검색하는 로직을 기록해둔다.

 

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
    @Override
    public PageResponseDTO<UserListDTO> getMemberList(PageRequestDTO pageRequestDTO) {
        /**
         * 성명이 DB에 암호화 저장되어 있어서 userNM 으로 Vue3(Front-End) 에서 데이터가 넘어온 경우에는 암호화하여 비교해야 한다.
        * 문제는 SafeDB Class 가 암호화할 때에는 SecureRandom random = new SecureRandom(); 를 사용하여
         * 데이터를 암호화 저장하기 때문에 단순하게 해결되지 않는다. claude 도움을 받아서 해결했지만 로직에 대한 충분한 이해와
         * 접근으로 중간 중간 요구사항을 전달하면서 해결해야지 무조건 믿고 알려줄 걸 기대하면 안된다는 점이다.
         * 아래와 같은 방법으로 문제를 해결하였다. Log 정보는 확인 용도
         */
 
        log.info("🔍 검색 조건: where={}, keyword=[{}]",
                pageRequestDTO.getFilter().getWhere(),
                pageRequestDTO.getFilter().getKeyword());
 
        // userNM 검색인지 확인
        boolean isUserNmSearch = "userNM".equals(pageRequestDTO.getFilter().getWhere())
                && pageRequestDTO.getFilter().getKeyword() != null
                && !pageRequestDTO.getFilter().getKeyword().isBlank();
 
        String searchKeyword = null;
 
        // userNM 검색 시 키워드 처리
        if ("userNM".equals(pageRequestDTO.getFilter().getWhere())) {
            String keyword = pageRequestDTO.getFilter().getKeyword();
 
            if (keyword == null || keyword.isBlank()) {
                // 빈 검색어면 조건 제거
                log.info("🚫 userNM 검색이지만 키워드가 비어있음 - 검색 조건 제거");
                pageRequestDTO.getFilter().setWhere(null);
                pageRequestDTO.getFilter().setKeyword(null);
            } else {
                // ✅ 검색어가 있으면 키워드 저장하고 SQL 조건 제거
                searchKeyword = keyword;
                log.info("🔍 userNM 검색 키워드 저장: [{}]", searchKeyword);
                pageRequestDTO.getFilter().setWhere(null);  // SQL에서 조건 제거
                pageRequestDTO.getFilter().setKeyword(null); // SQL에서 조건 제거
            }
        }
 
        // ✅ orgId 하위 포함 처리 - unit_code 기반으로 개선
        String unitCode = null;
        String orgIdStr = pageRequestDTO.getFilter().getOrgId();
 
        if (orgIdStr != null && !orgIdStr.isBlank()) {
            try {
                Integer orgId = Integer.parseInt(orgIdStr);
                if (orgId > 0) {
                    unitCode = hrOrgchartService.getUnitCodeForSubOrgSearch(orgId);
                    log.info("🔍 조직 검색: orgId={} -> unitCode=[{}]", orgId, unitCode);
                }
            } catch (Exception e) {
                log.warn("orgId 파싱 실패: {}", orgIdStr, e);
            }
        }
 
        // ✅ 데이터 조회 방식 분기
        List<UserListVO> voList;
        long totalCount;
 
        if (isUserNmSearch && searchKeyword != null) {
            // ✅ userNM 검색 시: 전체 데이터 조회 (페이징 없음)
            log.info("🔍 userNM 검색을 위해 전체 데이터 조회");
 
            if (unitCode != null && !unitCode.isEmpty()) {
                // unit_code 기반 전체 검색
                voList = memberMapper.selectAllMembersByUnitCode(unitCode);
            } else {
                // 기존 방식 전체 검색
                List<Integer> orgIds = new ArrayList<>();
                voList = memberMapper.selectAllMembers(orgIds);
            }
 
            log.info("🔍 전체 데이터 조회 완료: {}건", voList.size());
 
            // ✅ Java에서 필터링
            final String finalSearchKeyword = searchKeyword;
            List<UserListVO> filteredVoList = voList.stream()
                    .filter(vo -> {
                        try {
                            String decryptedName = encryptService.decryptUserNm(vo.getUserNm());
                            if (decryptedName == null) {
                                return false;
                            }
                            boolean matches = decryptedName.contains(finalSearchKeyword);
                            if (matches) {
                                log.debug("🔍 매칭 성공: [{}] contains [{}]", decryptedName, finalSearchKeyword);
                            }
                            return matches;
                        } catch (Exception e) {
                            log.warn("복호화 실패: memberId={}", vo.getMemberId(), e);
                            return false;
                        }
                    })
                    .collect(Collectors.toList());
 
            log.info("🔍 userNM 필터링 결과: {}건", filteredVoList.size());
 
            // ✅ 필터링된 결과에 페이징 적용
            totalCount = filteredVoList.size();
            int offset = (pageRequestDTO.getPage() - 1* pageRequestDTO.getSize();
 
            voList = filteredVoList.stream()
                    .skip(offset)
                    .limit(pageRequestDTO.getSize())
                    .collect(Collectors.toList());
 
            log.info("🔍 페이징 적용 후: {}건 (offset: {}, size: {})",
                    voList.size(), offset, pageRequestDTO.getSize());
 
        } else {
            // ✅ 일반 검색: 기존 페이징 방식
            int offset = (pageRequestDTO.getPage() - 1* pageRequestDTO.getSize();
 
            if (unitCode != null && !unitCode.isEmpty()) {
                voList = memberMapper.selectMemberListByUnitCode(pageRequestDTO, offset, unitCode);
                totalCount = memberMapper.countMemberListByUnitCode(pageRequestDTO, unitCode);
                log.info("🔍 unit_code 기반 검색 완료: {}건", voList.size());
            } else {
                List<Integer> orgIds = new ArrayList<>();
                voList = memberMapper.selectMemberList(pageRequestDTO, offset, orgIds);
                totalCount = memberMapper.countMemberList(pageRequestDTO, orgIds);
                log.info("🔍 기존 방식 검색 완료: {}건", voList.size());
            }
        }
 
        // VO → DTO 변환
        log.info("🔄 VO → DTO 변환 시작 ({}건)", voList.size());
 
        final List<UserListVO> finalVoList = voList;
        final long finalTotalCount = totalCount;
 
        List<UserListDTO> dtoList = IntStream.range(0, finalVoList.size())
                .mapToObj(i -> {
                    UserListVO vo = finalVoList.get(i);
                    // userNM 검색 시에는 순번 계산을 다르게
                    int no = isUserNmSearch ? (int)(finalTotalCount - ((pageRequestDTO.getPage() - 1* pageRequestDTO.getSize()) - i)
                            : (int)(finalTotalCount - ((pageRequestDTO.getPage() - 1* pageRequestDTO.getSize()) - i);
 
                    return UserListDTO.builder()
                            .no(no)
                            .memberId(cryptoUtil.encryptAES(String.valueOf(vo.getMemberId())))
                            .userId(vo.getUserId())
                            .userNm(encryptService.decryptUserNm(vo.getUserNm()))
                            .department(hrOrgchartService.getFullDepartmentPath(vo.getOrgId()))
                            .regNo(vo.getRegNo())
                            .admin(vo.getAdmin())
                            .orgId(vo.getOrgId())
                            .rnkCd(vo.getRnkCd())
                            .rnkNm(vo.getRnkNm())
                            .rspofcNm(vo.getRspofcNm())
                            .access(vo.getAccess())
                            .accessDate(String.valueOf(vo.getAccessDate()))
                            .createdAt(InputSanitizer.extractDateOnly(vo.getCreatedAt()))
                            .date(String.valueOf(vo.getDate()))
                            .lastLoginDate(InputSanitizer.extractDateOnly(vo.getLastLoginDate()))
                            .lastLoginGap(InputSanitizer.lastLoginGap(vo.getLastLoginDate()))
                            .build();
                })
                .collect(Collectors.toList());
 
        PageResponseDTO<UserListDTO> responseDTO = PageResponseDTO.<UserListDTO>withAll()
                .dtoList(dtoList)
                .pageRequestDTO(pageRequestDTO)
                .totalCount(finalTotalCount)
                .build();
 
        return responseDTO;
    }
 

 

728x90
블로그 이미지

Link2Me

,