Project

일반

사용자정보

DCAMP FRONTEND API 개발 가이드 » 이력 » 버전 22

최시은, 2023-05-31 01:38

1 1 최시은
h2. DCAMP FRONTEND API 개발 가이드
2
3 22 최시은
[[Guide|8. 검색 조건에 따른 목록 화면]]
4 20 최시은
5 3 최시은
1. SecurityConfig 설정(security/config/SecurityConfig.java)
6 1 최시은
*  securityFilterChain 메소드 - 개발 진행할 URL 에 대한 hasAuthority 룰 추가
7
<pre><code class="java">
8
http.csrf().disable()
9
     .authorizeHttpRequests((authorize) ->
10 2 최시은
           authorize
11
               // hasAuthority 사용: db에 ROLE_ 안붙여도 됨
12
              .requestMatchers("/projects/**").hasAuthority("ADMIN") // 관리자 : ADMIN | 작업자&검수자 : USER
13
              .anyRequest().authenticated()
14 1 최시은
</code></pre>
15 3 최시은
16
2. Swagger 사용
17
* 설정 파일 위치 : common/config/SwaggerConfig.java (이미 설정되어 있으므로 따로 추가사항 없음)
18
* 접속 URL : http://localhost:18080/swagger-ui/index.html
19
login-controller 에서 로그인 후, 응답 토큰을 우상단 Authorized 버튼 클릭해서 입력하면 인증 가능
20 4 최시은
21 8 최시은
3. Request URL Naming Rule
22 5 최시은
* RESTful API 기반 : https://restfulapi.net/resource-naming/
23
* 1 Depth : 대메뉴(ex, project, user 등등)
24
<pre>http://api.example.com/user-management/users
25
http://api.example.com/user-management/users/{id}</pre>
26 6 최시은
27
4. DTO 폴더
28 1 최시은
* 정의 : API 통신용 Request, Response 정의
29 8 최시은
* Naming Rule : HttpMethod + 대메뉴(복수 or 단수) or 주 사용 테이블 + Req/Res
30
<pre>
31
예시)
32
- GetProjectsReq : 프로젝트 목록 조회 요청
33
- GetProjectsRes : 프로젝트 목록 조회 응답
34
- PostProjectReq : 프로젝트 생성 요청
35
- 애매한 경우는 적당히 클래스명만 보고 가늠할 수 있게 지정해주세요
36
</pre>
37 6 최시은
* 공통 DTO 클래스
38
** CommonRes : 공통 응답필드(result, message) 정의, 상황에 따른 payload 세팅 가능
39
<pre><code class="java">
40
@PostMapping
41 12 최시은
public ResponseEntity<CommonRes> save(@AuthenticationPrincipal UserDetails userDetails, @RequestBody PostProjectReq req) throws Exception {
42
    Project project = projectService.createProject(req, userDetails);
43
    CommonRes response = project != null ? new CommonRes() : CommonRes.builder().result(false).message("오류가 발생했습니다.").build();
44
    return new ResponseEntity<>(response, HttpStatus.OK);
45
}
46 6 최시은
</code></pre>
47 7 최시은
** CommonListRes : 페이징 목록 조회 시 Response 객체로 사용
48
<pre><code class="java">
49
@GetMapping
50 12 최시은
public ResponseEntity<CommonListRes<GetProjectsRes>> list(GetProjectsReq req) throws Exception {
51
    CommonListRes<GetProjectsRes> response = projectService.list(req);
52
    return new ResponseEntity<>(response, HttpStatus.OK);
53
}
54 7 최시은
</code></pre>
55
** CommonSearch : 페이징 목록의 검색 시 Request 객체가 상속 받아 사용(페이징 정보 담김)
56
<pre><code class="java">
57
@AllArgsConstructor
58
@NoArgsConstructor
59
@Setter
60
@Getter
61
@EqualsAndHashCode(callSuper = false)
62
public class GetProjectsReq extends CommonSearch {
63 1 최시은
64 7 최시은
    private String projectStatusCcd;
65
    private String searchType;
66
    private String searchWord;
67
68
}
69
</code></pre>
70 13 최시은
** CommonPagination : 페이징 처리를 위해 CommonListRes에서 사용(따로 건드릴 일 없음)
71 9 최시은
72
5. Entity 폴더
73
* 정의 : JPA 에서 사용할 Entity 정의
74
75
6. DTO 객체와 Entity 객체 분리 사용
76
* 이유 : https://wildeveloperetrain.tistory.com/101
77
* DTO <-> Entity 는 BeanUtils.copyProperties 사용(필드명 일치시켜야함)
78
<pre><code class="java">
79
public Project createProject(PostProjectReq req, UserDetails userDetails) {
80 10 최시은
     Project saveProject = new Project();
81
     String userId = userDetails.getUsername();
82
     saveProject.setRegId(userId);
83
     // DTO <-> Entity 변환
84
     BeanUtils.copyProperties(req, saveProject);
85
     saveProject = projectRepository.save(saveProject);
86
     return saveProject;
87
}
88
</code></pre>
89
90
7. 현재 로그인한 아이디 가져오기(regId 에 주로 사용)
91
* Spring security 에서 제공하는 @AuthenticationPrincipal 사용해 UserDetails 객체 가져옴
92
* userDetails.getUsername() 으로 로그인ID 사용
93
<pre><code class="java">
94
@PostMapping
95
public ResponseEntity<CommonRes> save(@AuthenticationPrincipal UserDetails userDetails, @RequestBody PostProjectReq req) throws Exception {
96 11 최시은
    // String userId = userDetails.getUsername();
97 10 최시은
    Project project = projectService.createProject(req, userDetails);
98
    CommonRes response = project != null ? new CommonRes() : CommonRes.builder().result(false).message("오류가 발생했습니다.").build();
99
    return new ResponseEntity<>(response, HttpStatus.OK);
100
}
101 1 최시은
</code></pre>
102 14 최시은
103 22 최시은
[[8. 검색 조건에 따른 목록 화면]]
104 14 최시은
* Querydsl + 속도 개선을 위한 커버링 인덱스 활용
105
* 8번 참고 : https://velog.io/@youngerjesus/%EC%9A%B0%EC%95%84%ED%95%9C-%ED%98%95%EC%A0%9C%EB%93%A4%EC%9D%98-Querydsl-%ED%99%9C%EC%9A%A9%EB%B2%95
106
<pre><code class="java">
107
public CommonListRes<GetProjectsRes> list(GetProjectsReq req) {
108
        // 1. Querydsl 동적인 조건 검색을 위한 BooleanBuilder & PathBuilder 사용
109
        BooleanBuilder builder = new BooleanBuilder();
110
        builder.and(project.useYn.equalsIgnoreCase("Y"));
111
        PathBuilder<Project> pathBuilder = new PathBuilderFactory().create(Project.class);
112
        StringPath path;
113
114
        // 2. 검색 조건이 담긴 요청 객체의 필드를 순회 하면서 값이 있으면 조건 추가
115
        BeanWrapper beanWrapper = new BeanWrapperImpl(req);
116
        Field[] fields = GetProjectsReq.class.getDeclaredFields();
117
        for(Field field : fields){
118
            if("searchWord".equals(field.getName())) {
119
                continue;
120
            }
121
            if(StringUtils.isNotBlank((String) beanWrapper.getPropertyValue(field.getName()))){
122
                if("searchType".equals(field.getName())){
123
                    if(StringUtils.isNotBlank(req.getSearchWord())){
124
                        path = pathBuilder.getString(req.getSearchType());
125
                        builder.and(path.contains(req.getSearchWord()));
126
                    }
127
                } else {
128
                    path = pathBuilder.getString(field.getName());
129
                    builder.and(path.equalsIgnoreCase((String) beanWrapper.getPropertyValue(field.getName())));
130
                }
131
            }
132
        }
133
134
        // 3. 검색 조건이 반영된 ID 리스트 조회
135
        List<Integer> projectIds = jpaQueryFactory
136
                .select(project.projectId)
137
                .from(project)
138
                .where(builder)
139
                .orderBy(project.regDt.desc())
140
                .limit(req.getPageSize())
141
                .offset((long) req.getPageSize() * (req.getCurrentPageNo() - 1))
142
                .fetch();
143
144
        // 4. 검색된 ID 기반 전체 필드 조회 (속도 개선을 위한 커버링 인덱스 기법)
145
        List<Project> projects = projectIds.isEmpty()
146
                ? new ArrayList<>()
147
                : jpaQueryFactory
148
                .select(project)
149
                .from(project)
150
                .where(project.projectId.in(projectIds))
151
                .orderBy(project.regDt.desc())
152
                .fetch();
153
154
        // 5. Entity -> DTO
155
        List<GetProjectsRes> resultList = new ArrayList<>();
156
        projects.forEach(value -> resultList.add(GetProjectsRes.convertToDTO(value)));
157
158
        CommonPagination pagination = new CommonPagination();
159
        pagination.setCurrentPageNo(req.getCurrentPageNo());
160
        pagination.setRecordCountPerPage(req.getRecordCountPerPage());
161
        pagination.setPageSize(req.getPageSize());
162
        pagination.setTotalRecordCount(projects.size());
163
        return new CommonListRes<>(resultList, pagination);
164 20 최시은
    }
165 21 최시은
</code></pre>