플레이데이터 데이터 엔지니어링 28기 10~11주차 회고록

미난·2023년 12월 20일
0
post-thumbnail

안녕하세용~ 굉장히 오랜만입니다.
마지막 게시글을 보니 약 한 달만의 복귀더라고요.....
다시 스스로 기강 잡고자 회고록을 씁니다.

마지막 적은 글을 보니 자바 입문, 도커, MySQL, 약간의 프론트엔드 정도 했더라고요.
지금은 그것들을 더욱 공부하고 활용하여 실제 웹페이지를 만드는 미니 프로젝트 기간입니다.
아직 프로젝트가 마무리 되지 않아 오늘 마무리 글까지 쓰긴 힘들지만 지금까지 어떤 공부를 했고, 어떤 웹페이지를 만들고 있는지 적도록 하겠습니다.

가보자고~~~


주요 학습내용

Spring

Spring 프레임워크는 자바 기반의 오픈 소스 애플리케이션 프레임워크로, 엔터프라이즈급 애플리케이션을 개발하기 위한 다양한 기능을 제공합니다. 주로 엔터프라이즈급 소프트웨어 개발을 간소화하고 모듈성을 높이기 위해 사용됩니다.
Spring MVC는 모델-뷰-컨트롤러 아키텍처를 제공하여 웹 애플리케이션을 구축하는 데 사용됩니다. 이를 통해 유연하고 모듈식인 웹 애플리케이션을 개발할 수 있습니다.

DTO, DAO, Repository, Entity

(Spring Boot 서비스 구조)

Entity(Domain)
데이터베이스에 쓰일 컬럼과 여러 엔티티 간의 연관관계를 정의합니다.
데이터베이스의 테이블을 하나의 엔티티로 생각해도 무방합니다.
이 클래스의 필도는 각 테이블 내부의 컬럼(Column)을 의미합니다.

Repository
Entity에 의해 생성된 데이터베이스에 접근하는 메소드를 사용하기 위한 인터페이스 입니다.
Service와 DB를 연결하는 고리의 역할을 수행합니다.
데이터베이스에 적용하고자 하는 CRUD를 정의하는 영역입니다.

DAO(Data Access Object)
데이터베이스에 접근하는 객체를 의미합니다.
Service가 DB에 연결할 수 있게 해주는 역할입니다.
DB를 사용하여 데이터를 조회하거나 조작하는 기능을 전담합니다.

DTO(Data Transfer Object)
DTO는 VO(Value Object)로 불리기도 하며, 계층간 데이터 교환을 위한 객체를 의미합니다.
VO의 경우 Read Only의 개념을 가지고 있습니다.


Spring JPA

본격적인 JPA 설명에 앞서 ORM 에 대해 간략히 짚고 넘어가자면!

ORM(Object Relational Mapping)

  • 어플리케이션의 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑해주는 것을 의미합니다.
  • Java의 데이터 클래스와 관계형 데이터베이스의 테이블을 매핑합니다.
  • 객체지향 프로그래밍과 관계형 데이터베이스의 차이로 발생하는 제약사항을 해결해주는 역할을 수행합니다.
  • 대표적으로 JPA!!!

Java Persistence API (JPA)란?

  1. JPA는 객체와 관계형 데이터베이스 간의 매핑을 담당하는 ORM 기술 중 하나입니다. 즉, 자바 객체를 데이터베이스 테이블에 자동으로 매핑해주는 역할을 합니다.

  2. 애노테이션 기반 설정: JPA는 주로 애노테이션을 사용하여 객체와 데이터베이스 간의 매핑을 정의합니다. 이를 통해 설정이 간소화되고, 개발자는 더 객체지향적인 코드를 작성할 수 있습니다.

  3. 쿼리 언어 제공: JPA는 객체 지향 쿼리 언어인 JPQL(Java Persistence Query Language)를 제공하여 객체를 기반으로 한 쿼리 작성을 지원합니다.

Spring Data JPA

  • Spring Framework에서 JPA를 편리하게 사용할 수 있게 지원하는 라이브러리
    CRUD 처리용 인터페이스를 제공합니다.
    Repository 개발 시 인터페이스만 작성하면 구현 객체를 동적으로 생성해서 주입
    데이터 접근 계층 개발시 인터페이스만 작성해도 됩니다. 냐미~

Validation (유효성 검사)

서비스의 비즈니스 로직이 올바르게 동작하기 위해 사용되는 데이터에 대한 사전 검증하는 작업이 필요합니다.
Validation은 들어오는 데이터에 대해 의도한 형식의 값이 제대로 들어오는지 체크하는 과정을 뜻합니다.

일반적인 유효성 검사의 코드를 보여드릴게요.

if (itemDTO == null) {
    throw new IllegalArgumentException("아이템 정보가 존재하지 않습니다.");
}

if (itemDTO.getItemName() == null || itemDTO.getItemName().isEmpty()) {
    throw new IllegalArgumentException("아이템 명은 필수 입니다.");
}

if (itemDTO.getQuantity() == null) {
    throw new IllegalArgumentException("아이템 개수는 필수 입니다.");
}
  • 간단한 검증을 하더라도 검증관련 로직이 길어짐
  • 검증 로직이 중복으로 여러 Layer(Controller, Service, DAO 등)에 존재하게 됨
  • Layer에 검증 로직이 섞여있기 때문에 추적이 어렵고, 애플리케이션이 복잡해짐

이번엔 @Vaild를 이용한 유효성 검사 코드를 보여드릴게요.

public ResponseEntity<String> save(@Valid @RequestBody ItemDTO itemDTO) {
    itemService.save(itemDTO);
    return new ResponseEntity<>(HttpStatus.CREATED);
}


Spring 예외 처리(Exception)

  • @ControllerAdvice를 통해 모든 Controller에서 발생할 수 있는 예외를 처리합니다.
  • @exceptionHandler를 통해 특정 Controller의 예외를 처리합니다.

@ControllerAdvice로 모든 컨트롤러에서 발생할 예외를 정의하고, @ExceptionHandler를 통해 발생하는 예외 마다 처리할 메소드를 정의

@ExceptionHandler

@ExceptionHandler는 Controller계층에서 발생하는 에러를 잡아서 메서드로 처리해주는 기능이다.
Service, Repository에서 발생하는 에러는 제외한다.

@Controller
public class SimpleController {

    // ...

    @ExceptionHandler
    public ResponseEntity<String> handle(IOException ex) {
        // ...
    }
}
  • 여러개의 Exception 처리
    @ExceptionHandler의 value 값으로 어떤 Exception을 처리할 것인지 넘겨줄 수 있는데, value를 설정하지 않으면 모든 Exception을 잡게 되기 때문에 Exception을 구체적으로 적어주는 것이 좋다고 한다.
@Controller
public class SimpleController {

    // ...

    @ExceptionHandler({FileSystemException.class, RemoteException.class})
    public ResponseEntity<String> handle(Exception ex) {
        // ...
    }
}
  • 우선 순위
    Exception.class보다 구체적인 오류클래스(NullPointerException.class)가 우선순위 높음

@ControllerAdvice

@ControllerAdvice안에서 @ExceptionHandler를 사용하여 에러를 잡을 수 있다.

@ControllerAdvice
public class ExceptionHandlers {

    @ExceptionHandler(FileNotFoundException.class)
    public ResponseEntity handleFileException() {
        return new ResponseEntity(HttpStatus.BAD_REQUEST);
    }

}
  • 범위 설정
    @ControllerAdvice는 모든 에러를 잡아주기 때문에 일부 에러만 처리하고 싶을 경우에는 따로 설정을 해주면 된다.
// 1.
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}

// 2.
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}

// 3.
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}

@RestControllerAdvice

@RestControllerAdvice@ControllerAdvice@ResponseBody로 이루어져있어 오류 메세지를 Response body(즉, json)로 리턴할 수 있다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
	// ...	
}

Thymeleaf

Thymeleaf는 웹 및 웹이 아닌 환경 모두에서 작동할 수 있는 Java XML/XHTML/HTML5 템플릿 엔진입니다.
MVC 기반 웹 애플리케이션의 뷰 레이어에서 XML/XHTML/HTML5를 제공하는 데 더 적합하지만 오프라인 환경에서도 모든 XML파일을 처리할 수 있습니다.

타임리프 설정

  • build.gradle --> 의존성 주입
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
}
  • application.yml
spring:
  thymeleaf:
    predix: classpath:templates/thymeleaf/
    check-template-location: true 
    suffix: .html
    mode: HTML
    cache: false # default true, 개발 시에는 false로 두는 것이 좋음

타임리프를 적용한 HTML

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

</body>
</html>

자주 사용하는 타임리프 문법

타임리프에서 a태그를 작성할 때 예제들

<!-- 특정 url로 이동 -->
<a th:href="@{https://developer-rooney.tistory.com}">글 상세보기</a>

<!-- 현재 서버 내에서 이동 -->
<a th:href="@{/board/list}">게시글 리스트</a>

<!-- 파라미터를 넘길 시 -->
<a th:href="@{/board/view(id = ${board.id})}">글 상세보기</a>

<!-- 파라미터를 여러 개 넘길 시 -->
<a th:href="@{/board/view(id = ${board.id}, writer = ${board.writer}})}">글 상세보기</a>

<!-- PathVariable 사용 시 -->
<a th:href="@{/board/view/{id}(id = ${board.id})}">글 상세보기</a>

Mustache

Mustache는 템플릿 엔진(Template Engine) 중 하나로, 템플릿과 데이터를 이용하여 동적인 HTML을 생성하는 데 사용됩니다. 이는 주로 웹 애플리케이션에서 서버 측에서 동적인 콘텐츠를 생성할 때 활용됩니다.

  1. 템플릿 언어:
    Mustache는 간단하면서도 직관적인 템플릿 언어를 제공합니다. 이 언어를 사용하여 HTML 문서 내에 변수, 조건문, 반복문 등을 삽입할 수 있습니다.

  2. 데이터와의 결합:
    Mustache는 템플릿과 데이터를 결합하여 동적인 콘텐츠를 생성합니다. 서버 측에서 데이터를 가져와 템플릿에 적용하면, 클라이언트에게 전달되는 HTML은 동적으로 생성된 것이 됩니다.

  3. 간소한 문법:
    Mustache의 문법은 간소하고 일관성이 있어서 학습이 쉽습니다. 이 특징은 유지보수와 협업 측면에서 이점을 제공합니다.

  4. 언어 독립적:
    Mustache는 다양한 프로그래밍 언어에서 지원되는 표준 템플릿 언어로서, JavaScript, Python, Java, Ruby, 등 다양한 언어에서 사용할 수 있습니다.

  5. 로직의 분리:
    Mustache는 주로 뷰 (View)를 담당하며, 로직과의 분리를 강조합니다. 이는 MVC (Model-View-Controller) 아키텍처의 관점에서 관심사의 분리를 촉진하고, 코드의 가독성을 향상시킵니다.

Mustache는 최소한의 문법으로 템플릿 엔진을 구현하였기 때문에, 가볍고 단순한 프로젝트부터 대규모 웹 애플리케이션까지 다양한 환경에서 사용됩니다.

Mustache 설정

  • build.gradle --> 의존성 주입
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-mustache'
}
  • application.yml
spring:
  mustache:
    predix: classpath:templates/
    check-template-location: true 
    suffix: .mustache
    cache: false # default true, 개발 시에는 false로 두는 것이 좋음

Spring Security

인증(Authorization)과 인가(Authentication)

  • 인증(Authorization)
    해당 사용자가 본인이 맞는지를 확인하는 절차
  • 인가(Authorization)
    인증된 사용자가 요청한 자원에 접근 가능한지를 결정하는 절차

Spring Security는 Spring 기반의 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크이다.
Spring Security는 인증과 권한에 대한 부분을 Filter 흐름에 따라 처리하고 있다.
Spring Security는 보안과 관련해서 쳬계적으로 많은 옵션을 제공해주기 때문에 개발자 입장에서 일일이 보안관련 로직을 작성하지 않아도 된다는 장점이 있다.

Spring Security Configuration(스프링 보안 설정)

SecurityConfig

@Configuration
@EnableWebSecurity  // 스프링 시큐리티 필터가 스프링 필터체인에 등록이 됩니다.
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true) // @Secured 어노테이션 활성화, @PreAuthorize 어노테이션 활성화  
public class SecurityConfig {
  
  @Bean // 해당 메서드의 리턴되는 오브젝트를 IoC로 등록해준다.
  public BCryptPasswordEncoder encodePwd() {
    return new BCryptPasswordEncoder();
  }

  // Configuring HttpSecurity
  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

    http.csrf(AbstractHttpConfigurer::disable);
    http
      .authorizeHttpRequests(authorize -> authorize
        .requestMatchers("/user/**").authenticated() // 인증이되면 접근 가능 
        .requestMatchers("/manager/**").hasAnyAuthority("ADMIN", "MANAGER") // 인증&인가가 되면 접근 가능 
        .requestMatchers("/admin/**").hasAnyAuthority("ADMIN") // 인증&인가가 되면 접근 가능 
        .anyRequest().permitAll() // 누구나 접근 가능 
      )
      .formLogin(formLogin -> formLogin
        .loginPage("/loginForm")
        .loginProcessingUrl("/login") // login 주소가 호출이 되면, 시큐리티가 로그인 진행 
        .defaultSuccessUrl("/") // 시큐리티 로그인 성공시 해당 url 주소로 이동 
        .permitAll()
      );

    return http.build();
  }
  
}

UserDetails

  • Security Sessio -> Authentication -> UserDetails(정의)
public class PrincipalDetails implements UserDetails {

  private User user;

  public PrincipalDetails(User user) {
    this.user = user;
  }

  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    // TODO Auto-generated method stub
    Collection<GrantedAuthority> collect = new ArrayList<>();
    collect.add(new GrantedAuthority() {

      @Override
      public String getAuthority() {
        // TODO Auto-generated method stub
        return user.getRole();
      }
      
    });

    return collect;
  }
 ...
}

UserDetailsService

  • Security Sessio -> Authentication -> UserDetails(생성)
@Service
public class PrincipalDetailsService implements UserDetailsService {

  @Autowired
  private UserRepository userRepository;

  // 시큐리티 session -> Authentication -> UserDetails 
  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    // TODO Auto-generated method stub
    User userEntity = userRepository.findByUsername(username);
    log.info("[PrincipalDetailsService][loadUserByUsername] start");
    if (userEntity != null) {
      log.info(userEntity.toString());
      return new PrincipalDetails(userEntity);
    }

    return null;
  }
  
}

미니 프로젝트 시작

위에서 잠깐 언급했다시피 이제 국비지원 교육 과정 중 두 번째 미니 프로젝트가 시작됐습니다. 저번 미니 프로젝트의 경우 머신러닝/딥러닝&경진대회 였기에 국민대학교 경진대회에 참가했었죠.

이번 프로젝트는 웹사이트 제작이라는 공동의 목적을 두고 진행합니다.

각자 자신들의 웹사이트 아이디어를 어필하고 맞는 사람들끼리 삼삼오오 모여 팀을 구성했습니다. 이번 저희팀은 총 4명으로 팀장형의 아이디어에 반해 같이하자고 얘기했어요.

우리팀의 아이디어는 간략하게 아래와 같습니다.

현재 웹페이지를 구현하고 있습니다만 자세한 내용과 웹페이지 구성, 코드는 다음 회고록에서 다루도록 하겠습니다.


K-디지털플랫폼 AI 경진대회

전에 말했던 대로 저는 팀을 꾸려 해당 경진대회에 참가했습니다.
이 경진대회는 1차 예선과 본선이 나눠진 대회로 1차 예선을 통과해야만 본선을 들어갈 수 있었습니다.

1차 예선의 조건은 두 가지였습니다.
1. 마이크로소프트 자격증 AI-900 취득
2. 프로젝트 계획서 제출

먼저 AI-900은 취득했습니다!

하지만 다 끝난 와중에 아쉬운 건 1000점 만점 중 842점을 받은 것입니다.
누군가는 잘했다고 생각할 수 있지만, 개인적으로 시험 난이도를 생각했을땐 900점 이상을 기본으로 받았어야 하지 않았나 싶습니다.

그리고 프로젝트 계획서도 제출했습니다.
아래에 계획서의 내용을 전체적으로 캡쳐하긴 했지만 시간 단축을 위해 간단히 요약하자면!

  1. 장애인, 노약자 등과 같은 사회적 약자와의 디지털 격차를 줄이기 위해 AI 추천 알고리즘 플랫폼 개발
  2. 해당 추천 시스템은 사용자&보호자의 성향과 조건을 고려한 교육 및 구직 추천 시스템으로 정보 접근성을 향상시키는데 주력
  3. 공공데이터포털을 통해 필요한 데이터 GET
  4. Azure, Machine Learning 모델, 파이썬 언어를 통해 모델 개발 계획
  5. 웹사이트, 애플리케이션이 아닌 플랫폼에 사용될 모델을 개발하는게 최종 목표

자격증도 붙고, 계획서도 제출한 상태에서 과연 1차 예선을 붙었을까요?
결과는~ 아쉽게도 전 탈락했습니다.
여기서 "전" 이라고 붙힌 이유는 4명의 팀원 중 2명은 붙고, 저를 포함한 2명은 떨어졌기 때문입니다.
팀 출전을 했는데 어떻게 그럴수있지?! 라고 생각하실 수 있지만,,, 그럴수 있다고 하더라고요...
저도 놀랐습니다.
최종적으로 제가 탈락한 가장 큰 이유는 AI-900 자격증의 합격 점수가 낮았기 때문입니다. 제가 위에서도 언급했듯이 시험의 난이도는 굉장히 할 만했습니다. 기존에 머신러닝과 딥러닝을 알고계신 분들이라면 하루만 준비해도 볼 수 있을 정도의 난이도죠.

하지만 저의 안일함으로 합격은 했으나 고득점을 달성하진 못했고, 고득점을 받은 팀원들은 본선에 진출해 좋은 결과를 얻었습니다.
부러운 마음과 노력한 시간이 저의 멘탈을 마구 공격해 비록 좀 무너졌지만, 지금은 다시 일어나서 공부하고 있어요 ㅋㅋㅋㅋ
무엇보다 제가 시험을 잘 못봐서 떨어진건데 누구를 원망하고 누구를 탓하겠습니까!!
다 내 잘못이다!!!!!

무엇보다 경진대회 수상한 유정이, 수연이 너무 축하해

다음에도 해커톤이나 경진대회에 참여할 의사는 있습니다.
다음에 더 좋은 대회와 기회가 있다면 주저하지 않고 다시 시작하겠습니다.


잘한 점 & 아쉬운 점

일단 이번엔 잘한 점은 크게 없습니다. 정말로,,,,
공부도, 경진대회도, 건강관리도 이룬게 크게 없어 아쉬운 한 주 였습니다.
그래도 AI-900 자격증을 따서 다행이긴 합니다.

아쉬운 점은 반대로 많습니다.
일단 회고록을 오랫동안 쓰지 못한 점이 아쉬워요.
그리고 경진대회 준비하느라 Spring을 제대로 공부하지 못한 점도 아쉽습니다.
특히 이번 미니프로젝트를 하면서 자신의 역할을 제대로 차지 못한 저에게 실망했습니다.
꼭 다시 공부하고, 노력해서 극복하는게 저의 다음주 목표입니다.


마무리

스프링부터 소감까지 적다보니 참 글이 길어졌네요.
글이 이렇게 길어지지 않도록 미리미리 쓰고, 진도를 놓치지 않도록 해야겠습니다.
돌아오는 일요일까지 2개의 포스팅을 완료하도록 하겠습니다 (일단 저지르기)
앞으로 함께할 팀원들한테 미리 죄송하다는 말 전하고 싶고요.
간만에 쓴 회고록은 이쯤에서 마무리하겠습니다.

profile
개발이 하고 싶어요!

0개의 댓글