22-10-12~22-10-18 개인 스터디 회고록

BRINCE·2022년 10월 18일
0
post-thumbnail

서론

벌써 또 한주가 지나갔구나...

별로 뭐 한 것도 없는거 같은데... 😭

뭔가 이번 한주는 정신없이 지나간거 같다.

이번 한주는 크게 듣던 게시판 만들기 프로젝트 인강을 계속 들으면서 기능 다듬고.. 정말 게시판 이라고 할 수 있을만한 무언가를 만드는게 큰 목표였고..

부가적인건 작업하다가 지루하면 알고리즘 문제를 풀자! 가 작은 목표였다

게시판은 개인적으로 목표 달성!

이지만.. 알고리즘은 제쳐둔것 같다 ..

나는 확실히 뭘 만들고 다듬고 이쁘고 불편하지 않게 제 기능을 하는 무언가를 만드는걸 좋아하나보다 😮‍💨 (내 입장에선 거의 RPG 게임 캐릭터 키우면서 내실 잡고 능력 좋고 이쁘고 귀여운 아바타 입히는 느낌이랄까..)

본론

이번 한주는 크게 잡으면
게시판 대부분의 기능 완성하기!
였고 , 세부적인거는 이렇게 추가되었다.
(이번주부터는 디테일한 목표는 잡지 않고 나를 믿고 후리하게 진행해보자.. 라는 생각으로 그냥 하고싶은거 했다 😶🤥)

  • 스프링 시큐리티 디펜던시 추가하고 각 서비스에 접근 가능하도록 설정 클래스 작성하기
  • 게시판, 계정 Dto (Data Transfer Object), DtoResponse 작성해서 엔티티에 직접 접근하거나 직접 불러오지 않고 중간 통로 한개 더 만들기
  • 게시판 메인 페이지 모델 뷰로 건내준 ArticleResponse 정보 추출해서 게시글 리스트 출력하기
  • 단일 게시글 모델 뷰로 ArticleCommentsResponse 정보 받아 와서 게시글, 댓글 출력하기
  • 게시글, 댓글, 유저 서비스 생성해서 Dto 로 각각 CRUD 기능 구현 하기
  • 게시글 댓글 유저 컨트롤러 생성해서 Rest API 구현하기
  • Rest API 를 이용해 MVC 패턴으로 웹페이지에서 등록,삭제,출력,수정 기능 구현하기
  • BootStrap 으로 각각 기능에 쓰일 템플릿 작성/수정
  • (크게 나눠서) 게시글 작성,삭제,조회 / 게시글 댓글 등록,조회 / 회원가입,로그인
  • 스프링 시큐리티 이용하여 로그인 회원가입 기능 구현 , 세션 저장해서 댓글 작성자 , 게시글 작성자 현재 로그인한 유저 이름으로 입력하게끔 설정

와우 이렇게 적으니까 뭔가가 굉장히 많구나..!

이번주는 진짜 블로그에 TIL도 못쓸정도로 그냥 계속 뭐에 홀린듯이 만들고 편집하고 작성했다..

그래서 기록을 하나하나 못남긴게 너무 아쉬워서 지금이라도 조금씩 정리해보려고 한다 ㅎㅎ

스프링 시큐리티

스프링 시큐리티 디펜던시를 추가하면 자동적으로 Login 폼이 생성된다.
하지만 이 폼으로 인해서 게시판에 접근이 불가능하다. (마찬가지로 테스트코드 또한 실행이 불가능함)

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/**").permitAll()
            .and()
                .csrf().ignoringAntMatchers("/h2-console/**")
            ;
        return http.build();
    }
}

이렇게 스프링 시큐리티 config 클래스를 작성해주면 스프링 시큐리티로 인해서 접근이 불가능 했던 것들이 다시 접근이 가능해진다.
permitAll() 로 접근을 허용하고, csrf().ignoringAntMatchers("/h2-console/**") 는 "/h2-console/" 로 시작하는 URL은 CSRF 검증을 하지 않는다는 설정이다.

DTO (Data Transfer Object)

이름 그대로 직접적인 접근을 예방하고 , 안전한 징검다리를 놓는 느낌이라고 생각하면 될 것 같다.

저번주는 DTO 를 작성하면서 개발하기 전이라, 다른 기술 블로그 글들을 보면서 "왜 이사람은 편하게 엔티티로 직접 저장 삭제 같은걸 하지 않고 DTO를 만들어서 쓰지..?" 했는데 내가 DTO 배우기 전이라.. ㅋ

결론적으론 , 엔티티에 직접 접근하는 방식으로 설계를 하게 되면 , 모델과 뷰가 강하게 결합된다. 의존성이 높아진다는 것 같다. 그걸 좀더 느슨하게 해주는게 DTO 라고 한다 .

즉, 도메인 모델을 캡슐화 하여 보호할 수 있다.

근데 여기서 들었던 궁금점은 , DTO 를 각 도메인당 하나만 작성하고, 그 DTO 하나갖고 전송 전달 받을 수 있지 않나 ..?

어느 블로거 분들의 글을 보면 DTO Response 라는 하나의 DTO 를 가지고 전송 전달을 받지만,

내가 들었던 인강 강사님은 DTO 를 세개나 생성해서 ResponseDto, RequestDto , Dto 이렇게 세개를 쓰시는것 같았다...

이부분에 대해서는 단순히 사람들이 Dto를 쓰래서 Dto를 사용했을 뿐 아직 깊게 파고들지 못해서 조만간 날잡고 이런쪽에 대해서 공부를 해야겠다는 생각이 들었다.

MVC 패턴으로 CRUD 기능 구현하기

이번에 막무가내로 게시판을 만들면서 조금은 MVC 패턴이 뭔지 이해를 한 것 같다

말 그대로 MODEL VIEW CONTROLLER 인데 컨트롤러 주구장창 작성하고 있고.. 뷰 단 예민하게 하나하나 신경쓰면서 편집하고 있고.. 모델도 도메인 클래스 재설계 하면서 DTO 주구장창 수정하고 있으니 대충 감이 잡혔다.

아! 개발은 이렇게 하는거였구나..!

엔티티,DTO(모델) 를 작성하고, DTO를 전달받을 Service 를 만들고, Controller 를 통해서 DTO를 전달 받으면, 컨트롤러가 Service 에 DTO를 전달해 Service 는 그걸 가공해서 처리한다.

이렇게 생각하고 만드니 내 복붙 실력도 굉장히 많이 늘고(구글링 한거 어디에 갖다 붙여야 되는지 뭘 수정해야 되는지 분별 능력이 굉장히 상승함),

테스트코드 작성도 늘었다. 마찬가지로 이렇게 설계를 하니 유지보수도 굉장히 쉽다. 결국엔 다 객체를 이용해서 데이터를 전달하거나 저장 삭제 하는 느낌이라,
처음에는 객체 지향 프로그램이 정확히 뭐지.. 했는데 머리로는 이미 나만의 개념을 확립 시킨것 같다 ㅎㅎ 너무 좋은 발전이요

한가지 문제점은 처음에 급하게 강사님의 도메인 재설계를 따라가다보니 큰 변경점 하나를 놓친것 같더라.. 자꾸 Entity null 오류가 났다.. 설계상 문제가 없는거 같은데..? 했는데 생성자에서 뭐 하나가 놀고있더라 ; 며칠동안 고생했는데 단 한줄 추가로 모든게 풀렸다.

이때 문제는 테스트 코드 작성후에 동작이 제대로 안되는걸 단순히 나중에 고치면 되지~ 하고 넘겼다는 것이다.
이렇게 넘기면 언젠간 큰 고통이 따르게 될 것이니....

템플릿 작성하기


나는 백엔드 개발자인데.. 왜이렇게 앞단에 집착하지..?

싶은 생각이 들 정도로 이번주에 템플릿에도 신경을 굉장히 많이 썼다..

역시 전 예체능계였던 사람의 예민함은 절대 못참지..

부트스트랩으로 온갖 구글링과 복붙, 수정을 통해 나름대로 홈페이지 기존 테마에 맞게 수정하고 덧붙였다.

마찬가지로 페이지 추가로 인해 (회원가입 폼, 로그인폼, 게시글 작성 댓글 작성 폼) 폼 클래스를 하나 생성하고, 검증 기능을 넣어줬다.

컨트롤러에 매개값으로 폼 객체를 넣어주고, @Valid 어노테이션을 붙이고, 같이 BindingResults 객체를 넣어주면,
메소드 필드 내에 폼의 검증으로 인한 에러 발생시에 어떻게 행동할 것인지 설정 할 수 있고, 폼의 에러 메세지를 뷰에서 띄울 수 있다.

그리고 그 폼을 객체로 받아와서 컨트롤러에서 DTO를 작성후에 서비스로 넘겨주는 방식으로 메소드를 선언하니 굉장히 편했다. (정규 표현식 같은거는 나중에 써줘도 되지만 대체로 길이라던가 email 은 기본 어노테이션이 있어서 편하게 설정이 가능하다.)

헐 .. 나 나쁘지 않게 만든듯... ㅠ (그렇다고 해줘요.)

이 파트에서의 문제는 게시글작성, 회원가입, 로그인 폼에서는 문제가 없었지만,
댓글 작성에서 문제가 있었다.
게시글을 조회할때 던져주는 댓글 폼으로 게시글 ID를 받아와 굳이 url 에 PathVariable 변수를 추가하지 않고 댓글을 작성하도록 하고 싶었는데

내 현재 개발 지식과 타임리프 템플릿 운용법으로는 안되던것.. 자꾸 null 값을 받아와서 댓글 등록이 안됐다 ㅠㅠ.

그래서 어쩔 수 없이 그냥 PathVariable 로 기존에 받아왔던 ArticleID 를 값으로 넣어 서비스에 댓글DTO를 넘겨주는 방식으로 처리했다.

어떻게 보면 완벽하게 하려고 하기 보다는 첫 개발이니 아무래도 디테일한 것보다는 큰 기능 구현을 먼저 해놓고 추가하는게 나을거 같다는 생각이 들어 오래 잡아놓고 있진 않았다 .

로그인, 회원가입 기능 구현 후에 세션 활용한 글 작성 댓글작성 기능 활성화/비활성화

로그인이 안되어 있는데 작성을 하면 오류가 나야한다.. 왜냐하면 작성 폼에는 닉네임을 입력하는 칸이 없이 때문이다.. 댓글도 마찬가지이다.

그런데 입력 칸이 존재한다는건 말이 안된다는것!

그래서 로그인, 회원가입 기능을 구현하기로 맘을 먹었다.

봉인해뒀던 Spring Security를 활용하면 된다.

Spring Security 같은 경우엔 로그인 폼과 기능을 알아서 구현해줘서 뷰와 서비스 정도만 작성해주면 된다 .

그 전에

               .and()
                .headers()
                .addHeaderWriter(new XFrameOptionsHeaderWriter(
                        XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
                .and()
                .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/")
                .and()
                .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/")
                .invalidateHttpSession(true)

SecurityConfig 클래스에 미리 작성해뒀던 접근 허용 메소드 밑에 이 문구를 추가하면 된다.
여기에 로그인 페이지와 로그아웃 페이지를 내가 컨트롤러에 설정해둔 해둔 GetMapping url 로 등록하면
따로 로그인 로그아웃에 관련된 메소드를 작성하지 않아도 알아서 동작해준다. 거기에 로그인 세션까지 전달해주는건 덤 !

그렇게 똑같이 로그인 폼과 회원가입 폼 클래스를 작성한후에 검증 조건을 부여해 컨트롤러에 매개값으로 전달해준다.

폼으로 전달받은 값을 활용해 중복 검사도 할 수 있다.

그렇게 로그인과 회원가입 폼, 기능을 구현했다면 ! 이제 세션을 활용해 작성 기능을 활성화/비활성화 할 수 있게 구현이 가능하다.

스프링 시큐리티는 로그인을 할때 전달 값이 암호화된 비밀번호가 아니라면 null 값을 받아온다. 그래서 인코더를 사용해야 한다.

   public BCryptPasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }

이걸로 비밀번호를 setter로 암호화해주면 되지만.. 나같은 경우에는 이걸 사용하면 똑같이 null값을 받아왔다.

그래서 {noop} 을 붙여 텍스트 그대로 비밀번호를 받아오도록 해줬다.
( 이건 곧 수정할 예정! )

그리고 SecurityConfig 에

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

이걸 추가해주고 , UserSecurityService 를 작성해


@RequiredArgsConstructor
@Transactional
@Service
public class UserSecurityService implements UserDetailsService {
    private final UserAccountRepository userAccountRepository;

    @Override
    public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
            Optional<UserAccount> _account = userAccountRepository.findById(userId);
            if (_account.isEmpty()) {
                throw new UsernameNotFoundException("사용자를 찾을수 없습니다.");
            }
            UserAccount account = _account.get();
            List<GrantedAuthority> authorities = new ArrayList<>();
            if ("admin".equals(userId)) {
                authorities.add(new SimpleGrantedAuthority(UserAccountRole.ADMIN.getValue()));
            } else {
                authorities.add(new SimpleGrantedAuthority(UserAccountRole.USER.getValue()));
            }
            return new User(account.getUserId(), account.getUserPassword(), authorities);
        }
}

(로그인에 성공하면 계정 아이디와 비밀번호가 담긴 정보를 넘겨받는다.)

이걸 추가해주면 로그인 회원가입 기능이 완성되고 로그인 세션이 부여된다.

세션 정보를 얻는 법은

UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

이렇게 현재 로그인된 계정 아이디와 비밀번호가 담긴 정보를 얻어올 수 있다.

그리고 타임리프 문법으로 태그에 sec:authorize="isAuthenticated()"를 추가해주면

글 작성, 댓글작성, 로그아웃 버튼이 로그인 시에만 나타나게 된다.


(작성자와 세션의 아이디가 같으면 버튼이 수정 삭제 버튼이 나타남)

결론

오늘은 기존에 정리해둔 TIL 가 없어서 회고록에서 총정리를 해보았다.
더 쓸건 많지만 .. 길이상 생략 후에 문제가 있었던 부분들은 파트별로 정리를 살짝 해두었다.

이번주 얻어가는건 Spring Security 와 Dto 활용하는 방법. 그리고 MVC 디자인 패턴에 대해서 어느정도 익혔고, 어느 코드를 어디에 알맞게 배치해야되는지도 좀 익힌것 같다 ㅎ

다음 한주는 크게 조회수, 댓글 삭제 수정, 게시글 수정, 회원정보 수정, 관리자 계정 , 게시글 추천과 검색 정렬 정도를 구현할 예정이다 ㅎㅎ

앞으로도 화이팅!

profile
자스코드훔쳐보는변태

0개의 댓글