위켄 QNA 페이지
Q : 마켓앱에서는 타임리프를 사용하나요?
•
네 맞습니다.
Q : 리더앱은 언제 구현해야 하나요?
•
3주차 이후로 진행이 될 예정입니다.
Q : 리더앱은 별도의 프로젝트로 진행해야 하나요?
•
네 맞습니다.
•
인텔레제이에서 새 프로젝트를 만들어서 진행하시면 됩니다.
Q : 리더앱의 구현에서 훈련생들은 REST API 부분만 구현하면 되나요?
•
네 맞습니다.
•
클라이언트 부분, 즉 리액트 부분은 강사가 구현하여 제공합니다.
Q : 글을 편집하기 위한 에디터는 어떤걸 사용해야 하나요?
•
토스트에디터를 추천합니다.
Q : 4주간의 미션을 수월하게 수행한다면 어느정도의 실력을 갖추었다고 볼 수 있나요?
•
백엔드 개발자 1년차에 준하는 실력을 가졌다고 볼 수 있습니다.
Q : 요구사항 정의서의 ERD는 초안인가요?
•
네 초안입니다.
•
필요하다고 판단되는 엔티티를 추가해주세요.
Q : 요구사항 정의서의 ERD를 절대 고치면 안되나요?
•
그렇지 않습니다.
•
필요하다고 판단이 된다면 고쳐도 됩니다.
Q : 요구사항에 명시적으로 구현하라고 되어있지는 않지만 필요하다고 판단되는 기능들은 추가구현을 하면 되나요?
•
네 맞습니다.
•
단 오버엔지니어링과 과한 추상화는 피해주세요.
Q : 테스트케이스는 다다익선인가요?
•
네 맞습니다.
Q : 정해진 시간보다 빨리 끝냈습니다. 뭘 더 하면 될까요?
•
코드 퀄리티 개선(가독성 개선)과 테스트케이스를 조금 더 촘촘하게 구성하는 작업을 하시면 됩니다.
Q : 코드 퀄리티 개선과 테스트케이스 추가도 다 했다면 뭘 더 하면될까요?
•
또 다른 프로젝트 진행하시면 됩니다.
◦
개인적으로 해도 되고 팀을 꾸려서 진행해도 됩니다.
◦
이미 많은 분들이 별도의 프로젝트를 진행하고 계십니다.
Q : 가입시 nickname 을 입력하지 않으면 작가회원이 아닌 일반회원이 되나요?
•
네 맞습니다.
•
로그인 후 프로필 페이지에서 작가명을 입력하거나 변경할 수 있습니다.
•
작가명이 있어야 작가회원입니다.
Q : 프론트엔드 UI를 어떻게 구현할지는 개발자의 재량에 맡기나요?
•
네 맞습니다.
Q : 아이디찾기와 비밀번호찾기 페이지를 팝업창으로 구현해도 되는지 궁금합니다.
•
넵! 그렇게 해주시면 됩니다.
Q : 사용자에게 더 편하고 좋을 것 같은 방식이나 기능이 생각났을 때 그것을 프로젝트에 적용해도 되나요?
•
네. 좋습니다.
Q : 프론트엔드 UI를 예쁘게 잘 구현해야 하나요?
•
다른 우선순위 높은 작업들을 우선적으로 처리하고 여력이 되시면 UI 퀄리티를 높히는 작업을 하셔도 좋습니다.
•
UI 퀄리티는 낮아도 문제가 없습니다.
Q : ERD에 아주 사소한 문제가 있습니다. 이상하다고 느껴지더라도 그냥 진행해야 하나요?
•
아닙니다. ERD가 요구사항과 상충된다면, 요구사항 위주로 설계를 하시고 ERD를 변경해도 됩니다.
Q : 작가가 여러개의 상품을 등록할 수 있나요? ERD에서는 작가와 회원이 1:1 관계로 나와 있는 것 같아서요.
•
작가는 여러개의 상품을 등록할 수 있습니다.
•
ERD는 참고만 해주세요.
Q : 글과 관련된 테이블들의 관계가 궁금합니다.
•
글(Post)은 여러개의 글키워드(PostKeyword)를 가질 수 있습니다.
•
글과 글키워드를 연결해주는 중간 엔티티는 글태그(PostTag) 입니다.
•
하나의 postKeywordId 와 authorId 를 조합하면 특정회원이 특정 키워드에 관련해서 쓴 글을 알아낼 수 있습니다.
•
그 때문에 상품테이블에 authorId 와 postKeywordId 칼럼이 존재합니다.
Q : 각 주차별로 PR이 1개만 생성되어야 하나요?
•
네 맞습니다.
•
실무에서는 PR이 여러개 생기는게 더 바람직 하지만, 종합프로젝트 특성상 1개만 만드는 걸로 하겠습니다.
Q : 2주차 작업을 시작하기 전에, 깃 리포지터리에 2Week_Mission 폴더와 2Week_Record 폴더를 만들고 시작해야 하나요?
•
네 맞습니다.
•
본인의 1주차 작업물로 부터 작업을 이어 나가도 되고, 강사의 1주차 정답코드에서 이어 나가도 됩니다.
Q : 타임리프 부분, 즉 UI 부분도 꼭 구현해야 하나요?
•
네 맞습니다.
•
사용자가 이용하는게 가능하게 만들어야 합니다.
•
백엔드 개발자라도 기초적인 UI 구현은 무조건 할 수 있어야 합니다.
•
다만 부트스트랩이나 데이지UI는 사용하지 않아도 됩니다.
•
CSS 작업도 안해도 괜찮습니다.
Q : 작가가 책 판매 1권당 정산받을 금액을 산정하는 로직은 어떻게 되나요?
•
도매가격(wholesalePrice) - PG수수료(pgFee) 입니다.
Q : 요구사항 정의서 설명영상이 있나요?
Q : 스프링 DOC 힌트코드가 있나요?
package com.ll.exam.final__2022_10_08.app.springDoc;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import org.springframework.context.annotation.Configuration;
@Configuration
@OpenAPIDefinition(info = @Info(title = "MBOOKS API", version = "v1"))
@SecurityScheme(
name = "bearerAuth",
type = SecuritySchemeType.HTTP,
bearerFormat = "JWT",
scheme = "bearer"
)
public class SpringDocConfig {
}
Java
복사
package com.ll.exam.final__2022_10_08.app.myBook.controller;
import com.ll.exam.final__2022_10_08.app.base.dto.RsData;
import com.ll.exam.final__2022_10_08.app.base.rq.Rq;
import com.ll.exam.final__2022_10_08.app.myBook.dto.*;
import com.ll.exam.final__2022_10_08.app.myBook.entity.MyBook;
import com.ll.exam.final__2022_10_08.app.myBook.service.MyBookService;
import com.ll.exam.final__2022_10_08.util.Ut;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static org.springframework.http.MediaType.ALL_VALUE;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RestController
@RequestMapping(value = "/api/v1/myBooks", consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
@RequiredArgsConstructor
@Tag(name = "ApiV1MyBooksController", description = "로그인 된 회윈이 구매한 책 정보")
public class ApiV1MyBooksController {
private final MyBookService myBookService;
private final Rq rq;
@GetMapping(value = "", consumes = ALL_VALUE)
@Operation(summary = "로그인된 회원이 보유한 도서 목록", security = @SecurityRequirement(name = "bearerAuth"))
public ResponseEntity<RsData<MyBooksResponse>> myBooks() {
...
}
....
}
Java
복사
Q : 스프링 시큐리티 힌트코드가 있나요?
package com.ll.exam.final__2022_10_08.app.security;
import com.ll.exam.final__2022_10_08.app.security.filter.JwtAuthorizationFilter;
import com.ll.exam.final__2022_10_08.app.security.handler.ApiAuthenticationEntryPoint;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
@EnableWebSecurity
@RequiredArgsConstructor
public class ApiSecurityConfig {
private final JwtAuthorizationFilter jwtAuthorizationFilter;
private final ApiAuthenticationEntryPoint authenticationEntryPoint;
@Bean
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(authenticationEntryPoint)
)
.httpBasic().disable()
.csrf().disable()
.cors(cors -> cors
.configurationSource(corsConfigurationSource())
)
.authorizeRequests(
authorizeRequests -> authorizeRequests
.antMatchers("/api/*/member/login").permitAll()
.anyRequest()
.authenticated() // 최소자격 : 로그인
)
.sessionManagement(sessionManagement -> sessionManagement
.sessionCreationPolicy(STATELESS)
)
.formLogin().disable()
.addFilterBefore(
jwtAuthorizationFilter,
UsernamePasswordAuthenticationFilter.class
)
.logout().disable();
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/api/**", corsConfiguration);
return urlBasedCorsConfigurationSource;
}
}
Java
복사
package com.ll.exam.final__2022_10_08.app.security;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {
private final AuthenticationSuccessHandler authenticationSuccessHandler;
private final AuthenticationFailureHandler authenticationFailureHandler;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.formLogin(
formLogin -> formLogin
.loginPage("/member/login") // GET
.loginProcessingUrl("/member/login") // POST
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
)
.authorizeRequests(
authorizeRequests -> authorizeRequests
.antMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html")
.hasAuthority("ADMIN")
.anyRequest()
.permitAll()
)
.logout(
logout -> logout
.logoutUrl("/member/logout")
);
return http.build();
}
}
Java
복사