ORM 이란?

  ORM 이란 Object-Relational Mapping 의 약자로, 이름 그대로 객체(Object)와 관계형 데이터(Relational data) 를 매핑하기 위한 기술이다. 이러한 매핑이 필요한 이유는 객체 지향 언어과 관계형 데이터베이스사이의 패러다임 불일치가 있기때문이다.  이 둘 간의 패러다임 불일치 때문에 개발자는 더 많은 코드를 작성해야 하며, 이는 반복적이고 실수하기 쉬운 작업이 된다. 그렇기 때문에 개발자는 객체지향적인 설계에 집중할 수 없게 된다. ORM이 바로 이러한 문제를 해결해 준다. 

https://medium.com/@emccul13/object-relational-mapping-9d84807f5536

 

🌱 패러다임 불일치

 객체 지향 프로그래밍과 관계형 데이터베이스 사이의 데이터 표현 방식이 달라서 생기는 문제를 패러다임 불일치라고 한다. 패러다임 불일치가 일어나는 이유는 애초에 이들의 목표와 동작 방식이 다르기 때문이다.

  • 객체 지향
    • 필드와 메서드 등을 묶어서 객체로 잘 만들어 사용하는 것이 목표
    • 객체 지향 프로그래밍은 추상화, 캡슐화, 정보은닉, 상속, 다형성 등 시스템의 복잡성을 제어할 수 있는 다양한 장치들을 제공한다.
  • 관계형 데이터베이스
    • 데이터를 잘 정규화해서 보관하는 것이 목표

 

🌱 JPA

  JPA는 Java Persistence API의 약자로, 자바 ORM 기술에 대한 API 표준 명세이다. 즉, 인터페이스의 모음이다. 이러한 JPA 인터페이스를 구현한 대표적인 프레임워크가 하이버네이트(Hibernate)이다.JPA는 애플리케이션과 JDBC 사이에서 동작한다. 개발자가 JPA를 사용하면, JPA 내부에서 JDBC API를 사용하여 SQL을 호출하여 DB와 통신한다. 즉, 개발자가 직접 JDBC API를 쓸 필요가 없다.

 

🌱 Hibernate

 JPA를 구현한 프레임워크 중 사실상 표준이다. 오픈소스 소프트웨어이다. 여기서 주목해야할 점은 JPA 기술 스펙이고 하이버네이트는  기능을 구현하여 공급해주는 역할이다.

 

🌱 Spring Data JPA

 Spring framework에서 JPA를 편리하게 사용할 수 있도록 지원하는 프로젝트(모듈)이다. Spring Data JPA의 목적은 JPA를 사용할 때 필수적으로 생성해야하나, 예상가능하고 반복적인 코드들을 대신 작성해줘서 코드를 줄여주는 것이다. 이는 JPA를 한 단계 추상화시킨 Repository라는 인터페이스를 제공함으로써 이루어진다.

 Spring Data JPA는 JPA Provider이 아니다. 단지 데이터 계층 접근하기 위해 필요한 뻔한 코드들의 사용을 줄여주도록 하는 인터페이스이다. 여기서 반드시 기억해야할 점은 Spring Data JPA 항상 하이버네이트와 같은 JPA provider 필요하다는 것이다.

출처 : 스프링부트와 aws로 혼자 구현하는 웹서비스(이동욱 저)

'항해 99(9기) > 항해 일일' 카테고리의 다른 글

항해 99 30일차  (0) 2022.10.18
항해 99 29일차  (0) 2022.10.17
항해 99 23일차  (0) 2022.10.11
항해 99 20일차  (0) 2022.10.08
항해 99 19일차  (1) 2022.10.07

연관 관계 정의 규칙

연관 관계를 매핑할 때, 생각해야 할 것은 크게 3가지가 있습니다.

  • 방향 : 단방향, 양방향 (객체 참조)
  • 연관 관계의 주인 : 양방향일 때, 연관 관계에서 관리 주체
  • 다중성 : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)

하나 하나 생각해보겠습니다.

단방향, 양방향

데이터베이스 테이블은 외래 키 하나로 양 쪽 테이블 조인이 가능합니다.

따라서 데이터베이스는 단방향이니 양방향이니 나눌 필요가 없습니다.

그러나 객체는 참조용 필드가 있는 객체만 다른 객체를 참조하는 것이 가능합니다.

그렇기 때문에 두 객체 사이에 하나의 객체만 참조용 필드를 갖고 참조하면 단방향 관계, 두 객체 모두가 각각 참조용 필드를 갖고 참조하면 양방향 관계라고 합니다.

엄밀하게는 양방향 관계↔️는 없고 두 객체가 단방향 참조를 각각 가져서 양방향 관계처럼 사용하고 말하는 것입니다. ⬅️ ➡️

JPA를 사용하여 데이터베이스와 패러다임을 맞추기 위해서 객체는 단방향 연관 관계를 가질지, 양방향 연관 관계를 가질지 선택해야합니다.

선택은 비즈니스 로직에서 두 객체가 참조가 필요한지 여부를 고민해보면 됩니다.

  • Board.getPost()처럼 참조가 필요하면 Board→Post 단방향참조
    • 만약 참조가 굳이 필요없으면 참조를 안하면 됨
  • post.getBoard()처럼 참조가 필요하면 Post→Board 단방향참조
    • 만약 참조가 굳이 필요없으면 참조를 안하면 됨

이렇게 비즈니스 로직에 맞게 선택했는데 두 객체가 서로 단방향 참조를 했다면 양방향 연관 관계가 되는 것입니다.

단방향 연관 관계와 양방향 연관 관계를 구분하는 방법은 이렇게 이해하면 됩니다.

무조건 양방향 관계를 하면 쉽지 않나❓

객체 입장에서 양방향 매핑을 했을 때 오히려 복잡해질 수 있습니다.

예를 들어 일반적인 비즈니스 애플리케이션에서 사용자(User)엔티티는 굉장히 많은 엔티티와 연관 관계를 갖습니다.

이런 경우에 모든 엔티티를 양방향 관계로 설정하게 되면 사용자(User)엔티티는 엄청나게 많은 테이블과 연관 관계를 맺게 되고 User클래스를 보면 엄청나게 복잡해진 것을 확인할 수 있습니다.

그리고 다른 엔티티들도 불필요한 연관관계 매핑으로 인해 복잡성이 증가할 수 있습니다.

그래서 양방향으로 할지 단방향으로 할지 필히 구분해줘야합니다.

구분하기 좋은 기준은 기본적으로 단방향 매핑으로 하고 나중에 역방향으로 객체 탐색이 꼭 필요하다고 느낄 때 추가하는 것으로 잡으면 됩니다.

그냥 참조만 추가한다고 되는 건 아니고 자세한 것은 아래에서 설명합니다.

연관 관계의 주인

두 객체(A, B)가 양방향 관계, 다시 말해 단방향 관계 2개(A→B, B→A)를 맺을 때, 연관 관계의 주인을 지정해야 합니다.

연관 관계의 주인을 지정 하는 것은 두 단방향 관계(A→B, B→A)중, 제어의 권한(외래 키를 비롯한 테이블 레코드를 저장, 수정, 삭제 처리)을 갖는 실질적인 관계가 어떤 것인지 JPA에게 알려준다고 생각하면 됩니다.

 

연관 관계의 주인은 연관 관계를 갖는 두 객체 사이에서 조회, 저장, 수정, 삭제를 할 수 있지만, 연관 관계의 주인이 아니면 조회만 가능합니다.

연관 관계의 주인이 아닌 객체에서 mappedBy 속성을 사용해서 주인을 지정해줘야합니다.

TIP : 외래 키가 있는 곳을 연관 관계의 주인으로 정하면 됩니다. 무조건. 😄

왜 연관 관계의 주인을 지정해야하는가?

두 객체 (Board, Post)가 있고 양방향 연관 관계를 갖는다고 생각해봅니다.

그 상황에서 게시글(Post)의 게시판을 다른 게시판(Board)으로 수정하려고 할 때, Post 객체에서 setBoard(...) 같은 메소드를 이용해서 수정하는게 맞는지, Board객체에서 getPosts() 같은 메소드를 이용해서 List의 게시글을 수정하는게 맞는지 헷갈릴 수 있습니다. 🤔

두 객체 입장에서는 두 방법 다 맞는 방법이긴 합니다.

그러나 이렇게 객체에서 양방향 연관 관계 관리 포인트가 두 개일 때는 테이블과 매핑을 담당하는 JPA입장에서 혼란을 주게 됩니다.

즉, Post에서 Board를 수정할 때 FK(Foreign Key)를 수정할 지, Board에서 Post를 수정할 때 FK(Foreign Key)를 수정할 지를 결정하기 어려운 것입니다.

그렇기 때문에 두 객체 사이의 연관 관계의 주인을 정해서 명확하게 Post에서 Board를 수정할 때만 FK를 수정하겠다! 라고 정하는 것입니다.

연관 관계의 주인만 제어하면 되나?

데이터베이스에 외래 키가 있는 테이블을 수정하려면 연관 관계의 주인만 변경하는 것이 맞는가? 맞습니다.

맞긴 하지만, 그것은 데이터베이스만 생각했을 때고, 객체를 생각해보면 사실 둘 다 변경해주는 것이 좋습니다. (연관 관계의 주인이 아닌 곳에서도 변경!)

왜냐하면 두 참조를 사용하는 순수한 두 객체는 데이터 동기화를 해줘야하기 때문입니다.

 

다중성

데이터베이스를 기준으로 다중성을 결정합니다.

(JPA는 JPQL도 그렇고 보통 객체를 기준으로 하는게 일반적인데 다중성을 정하는 기준은 데이터베이스 기준인게 신기합니다.)

  • 연관 관계는 대칭성을 갖습니다.
    • 일대다 ↔ 다대일
    • 일대일 ↔ 일대일
    • 다대다 ↔ 다대다

다대일(N:1)

게시판(Board)과 게시글(Post)의 관계로 예를 들겠습니다.

  • 요구 사항
    • 하나의 게시판(1)에는 여러 게시글(N)을 작성할 수 있습니다.
    • 하나의 게시글은 하나의 게시판에만 작성할 수 있다.
    • 게시글과 게시판은 다대일 관계를 갖습니다.

데이터베이스를 기준으로 다중성(게시글N : 게시판1)을 결정했습니다.

즉, 외래 키를 게시글(N)이 관리하는 일반적인 형태입니다. (참고로 데이터베이스는 무조건 다(N)쪽이 외래 키를 갖습니다.)

다대일(N:1) 단방향

 

 

일대다(1:N)

어? 일대다는 다대일에서 반대 입장인데 정리할 필요가 있나? 생각할 수 있지만 앞서 다대일의 기준은 연관관계의 주인 다(N)쪽에 둔 것이고 이번에 언급할 일대다의 기준은 연관관계의 주인을 일(1)쪽에 둔 것입니다.

※ 참고로 실무에서는 일대다(1:N) 단방향은 거의 쓰지 않도록 합니다.

일대다(1:N) 단방향

데이터베이스 입장에서는 무조건 다(N)쪽에서 외래키를 관리합니다.

 

@OneToManymappedBy가 없어집니다. 양방향이 아니기 때문입니다.

대신 @JoinColumn을 이용해서 조인을 합니다.

 

다대다(N:N)

  • 실무 사용 금지 ❌
    • 중간 테이블이 숨겨져 있기 때문에 자기도 모르는 복잡한 조인의 쿼리(Query)가 발생하는 경우가 생길 수 있기 때문입니다.
    • 다대다로 자동생성된 중간테이블은 두 객체의 테이블의 외래 키만 저장되기 때문에 문제가 될 확률이 높습니다. JPA를 해보면 중간 테이블에 외래 키 외에 다른 정보가 들어가는 경우가 많기 때문에 다대다를 일대다, 다대일로 풀어서 만드는 것(중간 테이블을 Entity로 만드는 것)이 추후 변경에도 유연하게 대처할 수 있습니다.

출처: https://jeong-pro.tistory.com/231 [기본기를 쌓는 정아마추어 코딩블로그:티스토리]

'항해 99(9기) > 항해 일일' 카테고리의 다른 글

항해 99 29일차  (0) 2022.10.17
항해 99 25일차  (0) 2022.10.13
항해 99 20일차  (0) 2022.10.08
항해 99 19일차  (1) 2022.10.07
항해 99 18일차  (0) 2022.10.06

AOP(관점 지향 프로그래밍)란?

  • 부가기능을 모듈화
    • AOP (Aspect Oriented Programming) 를 통해 부가기능을 모듈화
      • **'부가기능'**은 '핵심기능'과는 관점(Aspect), 관심이 다름
      • 따라서 '핵심기능'과 또옥~!! 분리해서 '부가기능' 중심으로 설계, 구현 가능

  • 스프링이 제공하는 AOP

  1. Aspect: Advice + PointCut
  2. Advice: 어떤 부가기능을 언제?
  3. PointCut: 부가기능 적용 위치. 어느 패키지/메서드에?
부가기능을 핵심기능에서 필요로 할 때만 불러다 실행하고 다시 호출됐던 그 명령을 핵심 기능에 넘겨주는 역활

개념적으로는 수행전 수행후 혹은 두 모두에서 실행되는 매서드로 볼 수 있다.

실제 스프링 내부에서는 AOP 프록시라는 요소에 의해 사용자의 요청을 컨트롤러에 닿기 전 먼저 가로채 필요한 부가기능을 실행한 후에 다시 컨트롤러로 보내준다.

 

  • 스프링 AOP 어노테이션
    1. @Aspect - 스프링 빈 (Bean) 클래스에만 적용 가능
    2. 어드바이스 종류
      • @Around: '핵심기능' 수행 전과 후 (@Before + @After)
      • @Before: '핵심기능' 호출 전 (ex. Client 의 입력값 Validation 수행)
      • @After: '핵심기능' 수행 성공/실패 여부와 상관없이 언제나 동작 (try, catch 의 finally() 처럼 동작)
      • @AfterReturning: '핵심기능' 호출 성공 시 (함수의 Return 값 사용 가능)
      • @AfterThrowing: '핵심기능' 호출 실패 시. 즉, 예외 (Exception) 가 발생한 경우만 동작 (ex. 예외가 발생했을 때 개발자에게 email 이나 SMS 보냄)
    3. 포인트컷
      • 포인트컷 Expression Language
        • 포인트컷 Expression 형태
          • ? 는 생략 가능
        • execution(modifiers-pattern? return-type-pattern declaring-type-pattern? **method-name-pattern(param-pattern)** throws-pattern?)

포인트컷

  • 포인트컷 Expression Language
    • 포인트컷 Expression 형태
      • ? 는 생략 가능
    • execution(modifiers-pattern? return-type-pattern declaring-type-pattern? **method-name-pattern(param-pattern)** throws-pattern?)
    • 포인트컷 Expression 예제
    • @Around("execution(public * com.sparta.springcore.controller..*(..))") public Object execute(ProceedingJoinPoint joinPoint) throws Throwable { ... }
    • modifiers-pattern, 제어자
      • public, private, *
    • return-type-pattern, 반환타입
      • void, String, List<String>, *****
    • declaring-type-pattern, 클래스
      • 클래스명 (패키지명 필요)
      • com.sparta.springcore.controller.* - controller 패키지의 모든 클래스에 적용
      • com.sparta.springcore.controller.. - controller 패키지 및 하위 패키지의 모든 클래스
    • method-name-pattern(param-pattern)
      • 함수명
        • addFolders : addFolders() 함수에만 적용
        • add* : add 로 시작하는 모든 함수에 적용
      • 파라미터 패턴 (param-pattern)
        • (com.sparta.springcore.dto.FolderRequestDto) - FolderRequestDto 인수 (arguments) 만 적용
        • () - 인수 없음
        • (*) - 인수 1개 (타입 상관없음)
        • (..) - 인수 0~N개 (타입 상관없음)
    • @Pointcut
      • 포인트컷 재사용 가능
      • 포인트컷 결합 (combine) 가능

@Component

@Aspect

public class Aspect {

@Pointcut("execution(* com.sparta.springcore.controller.*.*(..))")

private void forAllController() {}

@Pointcut("execution(String com.sparta.springcore.controller.*.*())")

private void forAllViewController() {}

@Around("forAllContorller() && !forAllViewController")

public void saveRestApiLog() { ... }

@Around("forAllContorller()")

public void saveAllApiLog() { ... } }

 

'항해 99(9기) > 항해 일일' 카테고리의 다른 글

항해 99 25일차  (0) 2022.10.13
항해 99 23일차  (0) 2022.10.11
항해 99 19일차  (1) 2022.10.07
항해 99 18일차  (0) 2022.10.06
항해 99 17일차  (0) 2022.10.05

4주차 스프링 숙련 주차 시작!

 

RuntimeExeption과 Exception

 

  1. RuntimeException
  2. Exception

RuntimeException은 실행시 발생하는 예외이고 Exception은 컴파일시 발생하는 예외이다. 즉, Exception은 프로그램 작성시 이미 예측가능한 예외를 작성할 때 사용하고 RuntimeException은 발생 할수도 발생 안 할수도 있는 경우에 작성한다.

그래서 Exception을 Checked Exception, RuntimeException을 Unchecked Exception 이라고도 한다.

 

class FoolException extends Exception {
}

public class Sample {
    public void sayNick(String nick) {
        try {
            if("fool".equals(nick)) {
                throw new FoolException();
            }
            System.out.println("당신의 별명은 "+nick+" 입니다.");
        }catch(FoolException e) {
            System.err.println("FoolException이 발생했습니다.");
        }
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.sayNick("fool");
        sample.sayNick("genious");
    }
}

sayNick 메소드에서 try... catch 문으로 FoolException을 처리했다.

 

예외 던지기 (throws)

위 예제를 보면 sayNick 메서드에서 FoolException을 발생시키고 예외처리도 sayNick 메서드에서 했는데 이렇게 하지 않고 sayNick을 호출한 곳에서 FoolException을 처리하도록 예외를 위로 던질 수 있는 방법이 있다.

다음의 예제를 보자.

public class Sample {
    public void sayNick(String nick) throws FoolException {
        try {
            if("fool".equals(nick)) {
                throw new FoolException();
            }
            System.out.println("당신의 별명은 "+nick+" 입니다.");
        }catch(FoolException e) {
            System.err.println("FoolException이 발생했습니다.");
        }
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.sayNick("fool");
        sample.sayNick("genious");
    }
}

sayNick 메소드 뒷부분에 throws 라는 구문을 이용하여 FoolException을 위로 보낼 수가 있다. ("예외를 뒤로 미루기"라고도 한다.)

위와 같이 sayNick 메소드를 변경하면 main 메소드에서 컴파일 에러가 발생할 것이다. throws 구문 때문에 FoolException의 예외를 처리해야 하는 대상이 sayNick 메소드에서 main 메소드(sayNick 메소드를 호출하는 메소드)로 변경되었기 때문이다.

따라서 컴파일 오류를 해결하려면 다음과 같이 main 메소드를 변경해야 한다.

class FoolException extends Exception {
}

public class Sample {
    public void sayNick(String nick) throws FoolException {
        if("fool".equals(nick)) {
            throw new FoolException();
        }
        System.out.println("당신의 별명은 "+nick+" 입니다.");
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        try {
            sample.sayNick("fool");
            sample.sayNick("genious");
        } catch (FoolException e) {
            System.err.println("FoolException이 발생했습니다.");
        }
    }
}

main 메소드에서 try... catch로 sayNick 메소드에 대한 FoolException 예외를 처리하였다.

자, 이제 한가지 고민이 남아있다. FoolException 처리를 sayNick 메소드에서 하는것이 좋을까? 아니면 main 메소드에서 하는것이 좋을까? sayNick 메소드에서 처리하는 것과 main 메소드에서 처리하는 것에는 아주 큰 차이가 있다.

sayNick 메소드에서 예외를 처리하는 경우에는 다음의 두 문장이 모두 수행이된다.

sample.sayNick("fool");
sample.sayNick("genious");

main 메소드에서 try... catch로 sayNick 메소드에 대한 FoolException 예외를 처리하였다.

자, 이제 한가지 고민이 남아있다. FoolException 처리를 sayNick 메소드에서 하는것이 좋을까? 아니면 main 메소드에서 하는것이 좋을까? sayNick 메소드에서 처리하는 것과 main 메소드에서 처리하는 것에는 아주 큰 차이가 있다.

sayNick 메소드에서 예외를 처리하는 경우에는 다음의 두 문장이 모두 수행이된다.

sample.sayNick("fool");
sample.sayNick("genious");

물론 sample.sayNick("fool"); 문장 수행 시에는 FoolException이 발생하겠지만 그 다음 문장인 sample.sayNick("genious"); 역시 수행이 된다.

하지만 main 메소드에서 예외 처리를 한 경우에는 두번 째 문장인 sample.sayNick("genious");가 수행되지 않는다. 왜냐하면 이미 첫번 째 문장에서 예외가 발생하여 catch 문으로 빠져버리기 때문이다.

try {
    sample.sayNick("fool");
    sample.sayNick("genious");  // 이 문장은 수행되지 않는다.
}catch(FoolException e) {
    System.err.println("FoolException이 발생했습니다.");
}

이러한 이유로 프로그래밍시 Exception을 처리하는 위치는 대단히 중요하다. 프로그램의 수행여부를 결정하기도 하고 트랜잭션 처리와도 밀접한 관계가 있기 때문이다.

 

07-04 예외처리 (Exception) - 점프 투 자바 (wikidocs.net)

'항해 99(9기) > 항해 일일' 카테고리의 다른 글

항해 99 23일차  (0) 2022.10.11
항해 99 20일차  (0) 2022.10.08
항해 99 18일차  (0) 2022.10.06
항해 99 17일차  (0) 2022.10.05
항해 99 16일차  (0) 2022.10.04

[Spring] 회원 조회 API 만들기 (문제)

 
Goal : 회원목록 조회 API를 만들어봅시다.
지금까지 배운 내용을 바탕으로 간단한 회원 목록 조회 API를 만들어봅시다.
 
features : 구현해야 할 기능이에요.
회원 목록 DB는 다음과 같이 설계되어있어요.
테이블명 : Member
id : 회원 번호 (DB 인덱스)
name (String) : 회원 이름
email (String) : 회원 아이디
pw (String) : 회원 비밀번호
MemberInfoResponse.js
@Getter
@NoArgsConstructor
public class MemberInfoResponseDto {

    private Long id;
    private String name;
    private String email;
    private String pw;

    public MemberInfoResponseDto(Member member) {
        this.id=member.getId();
        this.name = member.getName();
        this.email= member.getEmail();
        this.pw=member.getEmail();
    }
}
 
 

commonResponse.js

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CommonResponse {
    private List<Object> result;

    public static CommonResponse response(List<Object> result){
        CommonResponse commonResponse = CommonResponse.builder().result(result).build();
        return commonResponse;
    }

}

MemberController.js

@RequiredArgsConstructor
@RestController
public class MemberController {

    private final MemberService memberService;
    /**
     * 한 회원의 userId가 주었을때 회원 정보를 조회하는 API
     * @param id
     */
    @GetMapping("/member/{id}")
    public CommonResponse getMemberInfo(@PathVariable Long id) {

        return memberService.findMember(id);
    }

    /**
     * 회원의 전체 목록을 조회하는 API
     */
    @GetMapping("/member")
    public CommonResponse getMemberList(){
        return memberService.findAllMember();
    }

}

MemberService.js

@Service
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;


    public CommonResponse findMember(Long memberId) {
        try {
            return CommonResponse.response(Collections.singletonList((memberRepository.findById(memberId).get())));
        }catch (NoSuchElementException e){
            
           return CommonResponse.response(Collections.singletonList("회원 목록 상세 조회 실패"));
        }
    }


    public CommonResponse findAllMember() {
        try {
            return CommonResponse.response(Collections.singletonList(memberRepository.findAll()));
        }catch (Exception e){
            return CommonResponse.response(Collections.singletonList("회원 목록 조회 실패"));
        }
    }
}

에러 시

{

  •  
  • result: [
    • "회원 목록 상세 조회 실패"
    ]

}

전반적으로 결과를 조회하고 성공 실패여부에 따라 status response를 양식을 만들어 넘겨주라는 문제였다.

위에 코드는 본인이 시험시간에 짠 코드이다.

 

밑에 코드는  시험 끝나고 끄적인것

내가 원하는 response 형식을 만들어 봤다.

CommonResponse.js

@Data
//@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CommonResponse<T> {
    private boolean success;

    private T result;

    private Error error;


//    public static CommonResponse response(Object data){
//        CommonResponse commonResponse = CommonResponse.builder().data(data).build();
//        return commonResponse;
//    }
    public static <T> CommonResponse<T> success(T data){
        return new CommonResponse<>(true,data,null);
    }
    public static <T> CommonResponse<T> successHeader(T data, HttpHeaders headers){
        return new CommonResponse<>(true, data, null);
    }
    public static <T> CommonResponse<T> fail(String code, String message){
        return new CommonResponse<>(false, null, new Error(code, message));
    }
    @Getter
    @AllArgsConstructor
    static class Error {
        private String code;
        private String message;

    }
}

{

  • success: false,
  • result: null,
  • error: {
    • code: "NO_SEARCH_ELEMENT",
    • message: "유저 상세조회를 할 수 없습니다."
    }

}

 

 

 

'항해 99(9기) > 항해 일일' 카테고리의 다른 글

항해 99 20일차  (0) 2022.10.08
항해 99 19일차  (1) 2022.10.07
항해 99 17일차  (0) 2022.10.05
항해 99 16일차  (0) 2022.10.04
항해 99 15일차  (0) 2022.10.03

Like 쿼리 예제

많은 간단한 LIKE 쿼리 시나리오의 경우 다양한 키워드를 활용하여 리포지토리에서 쿼리 메서드를 만들 수 있다.

 

NoticeRepository.js

 

List<Object> findAllByTitleContaining(String title);
List<Notice> findAllByTitleContains(String title);
List<Notice> findByTitleIsContaining(String title);

 

NoticeController.js

@GetMapping("/api/category/{category}")
public Object category(@PathVariable String category){
    return noticeService.category(category);
}

NoticeService.js

    @Transactional
    public  Object category(String category){
        return noticeRepository.findAllByTitleContains(category);
    }
}

http://localhost:8080/api/category/game 입력

결과:

    {
        "createdAt""2022-10-05T19:17:35.303884",
        "modifiedAt""2022-10-05T19:17:35.303884",
        "id"68,
        "title""game",
        "writer""jossi",
        "content""게임을 합시다."
    },

'항해 99(9기) > 항해 일일' 카테고리의 다른 글

항해 99 19일차  (1) 2022.10.07
항해 99 18일차  (0) 2022.10.06
항해 99 16일차  (0) 2022.10.04
항해 99 15일차  (0) 2022.10.03
항해 99 13일 차  (0) 2022.10.01

+ Recent posts