| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
- crud
- Algorism study
- MVC
- Java
- group study
- GIT
- #javaStudy
- MegabyteSchool
- MVC 패턴
- 게시판 만들기
- array
- Interface
- 클래스 상속
- tomcat
- #패스트캠퍼스 #국비지원교육 #메가바이트스쿨 #MegabyteSchool #개발자취업부트캠프 #내일배움카드
- Entity
- 내일배움카드
- AWS
- 메가바이트스쿨
- side project
- Spring
- github
- View
- spring boot
- 개발자취업부트캠프
- 게시판 리뷰 만들기
- 패스트캠퍼스
- Sts
- 클래스 class
- 국비지원교육
- Today
- Total
tuter77
2023.01.03 TIL 본문
<목차>
<공부계획표>
13:00 ~ 14:00 그룹스터디
게시판 페이징 처리 테스트 및 코드 수정.
과제 할당 - ManyToOne 조사(JPA 연관관계도 포함해서 정리)해서 블로그에 정리 > 노션에 링크달기.
14:00 ~ 18:00 (3시간) 온라인강의 시청.
14:00 ~ 15:00 Java 수강
15:00 ~ 16:00 Java 수강
17:00 ~ 18:00 Java 수강
18:00 ~ 19:00 식사시간
19:00 ~ 22:00 온라인 강의 시청
19:00 ~ 20:00 Spring 수강
20:00 ~ 21:00 Spring 수강
21:00 ~ 22:00 Spring 수강
22:00 ~ 23:00 블로그 작성 및 프로그래머스 문제 풀기.
<Java>
● super 키워드
- 하위 클래스에서 가지는 상위 클래스에 대한 참조 값
- super() 는 상위 클래스의 기본 생성자를 호출 한다.
- 하위 클래스에서 명시적으로 상위 클래스의 생성자를 호출하지 않으면, super()가 호출된다.(이때 반드시 상위 클래스의 기본 생성자가 존재해야한다.)
- 상위 클래스의 기본생성자가 없는 경우에(다른 생성자가 있는 경우) 하위 클래스에서는 생성자에서는 super를 이용하여 명시적으로 상위 클래스의 생성자를 호출한다.
- super는 생성된 상위 클래스 인스턴스의 참조 값을 가지므로 super를 이용하여 상위 클래스의 메서드나 멤버변수에 접근할 수 있다.
정리하면 하위클래스가 생성될 때는 무조건 상위클래스가 먼저 생성되고 호출되는데, 아무것도 없으면 super()를 자바가 자동으로 제공해서 default 생성자를 호출하게 된다.
그러나 default 생성자가 없는 경우에 개발자가 직접 명시적으로 호출해야한다.
예시)
// public Customer() {
//
// customerGrage = "SILVER";
// bonusRatio = 0.01;
//
// System.out.println("Customer() call");
//
// }
public Customer(int customerId, String customerName) {
this.customerId = customerId;
this.customerName = customerName;
customerGrage = "SILVER";
bonusRatio = 0.01;
System.out.println("Customer(int, String) call");
}
Default Constructor를 주석처리하고 아래에 명시적으로 만든 Customer 클래스 Constuctor.
// public VIPCustomer() { //자바에서 자동으로 default 생성자를 호출하는 super()를 암묵적으로 넣어줌.
//
// //super(0, "no-name"); // 상위 클래스 호출
// bonusRatio = 0.05;
// salesRatio = 0.1;
// customerGrage = "VIP";
//
// System.out.println("VIPCustomer() call");
// }
public VIPCustomer(int customerId, String customerName) { //디폴트 생성자를 주석처리하니 상위 클래스 호출 에러가 나서 자바에서 자동으로 제공.
super(customerId, customerName);
customerGrage = "VIP";
salesRatio = 0.1;
bonusRatio = 0.05;
System.out.println("VIPCustomer(int, String) call");
}
상위 클래스의 Default Constructor가 없어지니 기존의 super()가 제공된 상위클래스 호출구문에 에러가 떴다.
때문에 기존의 코드를 주석처리하고,super를 이용하여 상위클래스의 생성자를 명시적으로 호출하는 코드이다.
public static void main(String[] args) {
Customer customerLee = new Customer(10010, "이순신"); //
customerLee.bonusPoint = 1000;
System.out.println(customerLee.showCustomerInfo());
VIPCustomer customerKim = new VIPCustomer(10020, "김유신");
customerKim.bonusPoint = 10000;
System.out.println(customerKim.showCustomerInfo());
}
기존의 테스트 코드 역시 Default Constructor를 참조하고 있었기 때문에 오류가 발생해 위와 같이 새로 만든 생성자에 맞춰 수정된 코드이다.
▷ 상속에서 인스턴스 메모리의 상태
- 항상 상위 클래스의 인스턴스가 먼저 생성되고 하위 클래스의 인스턴스가 생성된다.

▷형변환(업캐스팅)
- 하위 클래스는 상위 클래스의 속성을 모두 갖고있으므로, 하위 클래스의 생성자를 상위 클래스로 변수를 선언하고 인스턴스를 생성하는 것이 가능하다.
예시) Customer customerLee = new VIPCustumer();
- 상위 클래스 타입의 변수에 하위클래스 변수가 대입하는 것도 가능하다.
VIPCustomer vCustomer = new VIPCustomer();
addCustomer(vCustomer);
int addCustomer(Customer customer){
}
정리하면, 하위 클래스는 상위 클래스의 타입을 내포하고 있으므로 상위 클래스로의 묵시적 형 변환이 가능하다.
상속 관계에서 모든 하위 클래스는 상위 클래스로 형 변환(업캐스팅)이 된다.
이를 예제에서 활용해보면 상위클래스 타입으로 형변환된 하위클래스의 변수는 상위클래스의 메서드만 사용이 가능한 것을 확인할 수 있다.
만약 해당 변수가 하위클래스의 메서드를 사용해야한다면, 다운캐스팅으로 다시 형변환 해야한다.
(이는 지정된 메모리 타입의 문제이기 때문.)


● 메서드 재정의 (Overriding)
<Spring >
● 프로그램 실행 결과 브라우저에 출력하기.
▷요일 테스트 프로그램 만들기
- 날짜 연월일을 입력하면 해당 요일을 반환해주는 프로그램.
public class YoilTeller {
public static void main(String[] args) {
//년월일을 입력하면 요일을 알려주는 프로그램
//1. 입력.
String year = args[0];
String month = args[1];
String day = args[2];
int yyyy = Integer.parseInt(year);
int mm = Integer.parseInt(month);
int dd = Integer.parseInt(day);
// 2. 작업
Calendar cal = Calendar.getInstance();
cal.set(yyyy, mm - 1, dd );
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
char yoil = "일월화수목금토".charAt(dayOfWeek);
//3. 출력
System.out.println(year + "년 " + month + "월 " + day + "일은 ");
System.out.println(yoil + "요일입니다.");
}
}
실행방법은 target 폴더 우클릭 > show in local terminal > 터미널창에서 classes 폴더로 이동후(cd classes) > java (패키지명)com.fastcampus.ch2.YoilTeller 2021 10 01(원하는 날짜 입력) 을 통해 프로그램을 터미널창에 실행시킨다.
실행 순서를 보면 java 인터프리터가 YoilTeller 클래스의 메인을 호출한다.
2021 10 1 문자열을 배열에 저장한다.
"2021" = args[0]
"10" = args[1]
"1" = args[2]
이 문자열을 args[]에 넘겨준다. 이 args가 문자열 배열을 가리키게 되어 main 메서드 안에서 문자열 배열을 쓸수 있게 된다.
▷ Http 요청과 응답.
1. HttpSerevletRequest - url에서 서버에 요청을 보내면 톰캣 서버가 HttpSerevletRequest 객체를 만들어 요청한 정보를 담는다.해당 객체를 메인 메서드의 매개변수로 넘겨줌. main(HttpSerevletRequest request);
위 YoilTeller 와 같은 방식으로 동작하는 것을 알 수 있다.
request 객체를 요청정보를 얻을 수 있다.
2. HttpSerevletRequest의 메서드 참조
package com.fastcampus.ch2;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class RequestInfo {
@RequestMapping("/requestInfo")
// public static void main(String[] args) {
public void main(HttpServletRequest request) {
System.out.println("request.getCharacterEncoding()="+request.getCharacterEncoding()); // 요청 내용의 인코딩
System.out.println("request.getContentLength()="+request.getContentLength()); // 요청 내용의 길이. 알수 없을 때는 -1
System.out.println("request.getContentType()="+request.getContentType()); // 요청 내용의 타입. 알 수 없을 때는 null
System.out.println("request.getMethod()="+request.getMethod()); // 요청 방법
System.out.println("request.getProtocol()="+request.getProtocol()); // 프로토콜의 종류와 버젼 HTTP/1.1
System.out.println("request.getScheme()="+request.getScheme()); // 프로토콜
System.out.println("request.getServerName()="+request.getServerName()); // 서버 이름 또는 ip주소
System.out.println("request.getServerPort()="+request.getServerPort()); // 서버 포트
System.out.println("request.getRequestURL()="+request.getRequestURL()); // 요청 URL
System.out.println("request.getRequestURI()="+request.getRequestURI()); // 요청 URI
System.out.println("request.getContextPath()="+request.getContextPath()); // context path
System.out.println("request.getServletPath()="+request.getServletPath()); // servlet path
System.out.println("request.getQueryString()="+request.getQueryString()); // 쿼리 스트링
System.out.println("request.getLocalName()="+request.getLocalName()); // 로컬 이름
System.out.println("request.getLocalPort()="+request.getLocalPort()); // 로컬 포트
System.out.println("request.getRemoteAddr()="+request.getRemoteAddr()); // 원격 ip주소
System.out.println("request.getRemoteHost()="+request.getRemoteHost()); // 원격 호스트 또는 ip주소
System.out.println("request.getRemotePort()="+request.getRemotePort()); // 원격 포트
}
}
[실행결과] http://localhost:8080/ch2/requestInfo?year=2021&month=10&day=1
request.getCharacterEncoding()=UTF-8
request.getContentLength()=-1
request.getContentType()=null
request.getMethod()=GET
request.getProtocol()=HTTP/1.1
request.getScheme()=http
request.getServerName()=localhost
request.getServerPort()=8080
request.getRequestURI()=http://localhost:8080/ch2/requestInfo
request.getRequestURI()=/ch2/requestInfo
request.getContextPath()=/ch2
request.getServletPath()=/requestInfo
request.getQueryString()=year=2021&month=10&day=1
request.getLocalName()=localhost
request.getLocalPort()=8080
request.getRemoteAddr()=0:0:0:0:0:0:0:1 <--- AWS에 배포(deploy)한 다음에 실행하면, 실제 ip주소를 확인할 수 있음.
request.getRemoteHost()=0:0:0:0:0:0:0:1 <--- AWS에 배포(deploy)한 다음에 실행하면, 실제 ip주소를 확인할 수 있음.
request.getRemotePort()=54855
- url 내의 ? 이후는 getQueryString() 메서드인데 이는 모두 문자열이다.
YoilTeller 로컬 프로그램을 원격으로 바꿔주면 아래와 같이 된다.
import java.util.Calendar;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class YoilTeller {
// public static void main(String[] args) {
@RequestMapping("/getYoil")
public void main(HttpServletRequest request) {
//년월일을 입력하면 요일을 알려주는 프로그램
//1. 입력.
String year = request.getParameter("year");
String month = request.getParameter("month");
String day = request.getParameter("day");
@Controller와 @RequestMapping 애노테이션을 달아주고, String year, month, day는 getparameter를 사용해 요청한다.
웹브라우저 url상에서

이와 같이 각 매개변수들을 지정하여 입력해주면, console창에 정상적으로 작동하는 것을 확인할 수 있다.
- HttpServletRequest,Response를 활용하여, 콘솔창이 아닌 브라우저에서 출력하는 것으로 변환하는것은 아래와 같다.
package com.fastcampus.ch2;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class YoilTeller {
// public static void main(String[] args) {
@RequestMapping("/getYoil")
public void main(HttpServletRequest request, HttpServletResponse response) throws IOException{
//년월일을 입력하면 요일을 알려주는 프로그램
//1. 입력.
String year = request.getParameter("year");
String month = request.getParameter("month");
String day = request.getParameter("day");
// 2. 작업
int yyyy = Integer.parseInt(year);
int mm = Integer.parseInt(month);
int dd = Integer.parseInt(day);
Calendar cal = Calendar.getInstance();
cal.set(yyyy, mm - 1, dd );
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
char yoil = " 일월화수목금토".charAt(dayOfWeek);
//3. 출력
response.setContentType("text/html"); //텍스트
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();// response 객체에서 브라우저로의 출력 스트림을 얻는다.
out.println(year + "년 " + month + "월 " + day + "일은 ");
out.println(yoil + "요일입니다.");
}
}
메인의 매개변수에 HttpServletResponse 매개변수를 추가해주고, 입출력 예외처리를 위해 throws IOException을 첨하고 import 해준다.
출력구문을 response 객체의 setContentType과 setCharaterEncoding으로 html 텍스트 타입임을 지정해주고, 바이너리는 utf-8을 사용하는 것을 알려준다.
콘솔에 출력하는 System.out.println() 구문을 PrintWriter 패키지로 수정하여 response 객체에서 브라우저로의 출력 스트림을 얻는다.
직후 printerWriter의 기능 중 하나인 println으로 브라우저에 괄호안의 내용을 출력해준다.
결과창.

<그룹스터디>
● ManyToOne
실제로 서비스되는 웹 애플리케이션에서 Member 엔티티와 Team 엔티티가 있을 때, 하나의 Team은 여러 멤버를 갖는 관계를 갖고 있다. 이 엔티티들의 관계를 매핑하는 것은 객체의 참조와 테이블의 외래키를 매핑하는 것을 의미한다.JPA에서 연관관계에 있는 상대 테이블의 PK를 멤버변수로 갖지않고, 엔티티 객체 자체를 통째로 참조한다고 한다.이 매핑을 이해하기 위한 3가지 키워드를 보겠다.
- 단방향 관계 : 두 엔티티가 관계를 맺을 때 한쪽의 엔티티만 참조하고 있다.
- 양방향 관계 : 두 엔티티가 관계를 맺을 때 양 쪽이 서로 참조하고 있다.
이 관계들은 ManyToOne, OneToMany, OneToOne, ManyToMany 중 하나의 관계를 갖게 되는데, 나는 이 중 ManyToOne을 조사하게 되었다.연관 관계의 주인은 외래키를 갖게되는 테이블이 연관관계의 주인이된다. 예를 들어

위와 같은 테이블 관계에서, FK 즉 외래키를 가진 MEMBER 테이블이 TEAM테이블의 주인이 된다.
주인테이블은 외래키를 관리(등록, 수정, 삭제) 할 수 있고, 주인이 아닌 엔티티는 읽기만 할 수 있다.
▷ 사용방법
@Entity
@Getter @Setter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String username;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
@Entity
@Getter @Setter
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
}
위의 코드를 보면 Member 엔티티에만 @ManyToOne 어노테이션이 있다.
연관관계를 매핑할 때 이렇게 다중성을 나타내는 어노테이션은 필수로 사용해야 하며, 엔티티 자신을 기준으로 다중성을 생각해야 한다.
@JoinColumn(name = "TEAM_ID")
이 부분이 외래키를 표현하는 어노테이션인데, @JoinColumn 어노테이션은 외래 키를 매핑할 때 사용한다. name 속성에는 매핑 할 외래 키 이름을 지정한다.
Member 엔티티의 경우 Team 엔티티의 id 필드를 외래 키로 가지므로, TEAM_ID를 작성했다고 한다.
여기서 양방향 연관관계로 전환 하려면, @OneToMany 애노테이션을 Team에 넣으면 된다.
@Entity
@Getter @Setter
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
}
위의 예시에서보면, 기존의 ManyToOne 관계에서 @OneToMany가 추가되어 양방향 관계가 되었다.
또한 Team은 Member를 list로 가지는 것을 볼 수 있다.
괄호 안의 mappedBy는 양방향 관계에서 주인을 지정해주는 구문이다.주인은 mappaedBy 속성을 사용하지 않고, @JoinColumn을 사용하기때문에 구분하기도 쉽다.이렇게 하는 이유가 더 있는데, 이는 Team DB에 members 외래키 컬럼을 만들지 않기위해서 이기도하다.
▷ OneToMany와 로딩방식의 차이
JPA에서 연관관계를 조회할때 조회 시점을 선택할 수 있도록 로딩방식 두가지를 제공한다.
- 즉시 로딩: 엔티티를 조회할 때 연관된 엔티티도 함께 조회한다.
em.find(Member.class, "member1")를 호출할 때 회원 엔티티와 연관된 팀 엔티티도 함께 조회합니다.
설정방법: @ManyToOne(fetch = FetchType.EAGER)
- 지연 로딩: 연관된 엔티티를 실제 사용할 때 조회한다.
member.getTeam().getName()처럼 조회한 팀 엔티티를 실제 사용하는 시점에 JPA가 SQL을 호출해서 팀 엔티티를 조회합니다.
설정방법: @ManyToOne(fetch = FetchType.LAZY)
이 로딩 방식에 따라 쿼리문의 형태가 달라진다.
이 쿼리문의형태는 아래에 나올 N+1문제와 직결되는데, 즉시로딩을 사용할 경우, member 하나를 조회할 때 연관팀 Team 을 무조건 같이 조회하게된다.
만약 Team이 하나가 아닌 100개라면 member 하나 조회하겠다고 100개의 팀을 조회하는 쿼리문이 같이 날아가게된다. 때문에 필요한 시점에만 Team을 조회할 수 있도록, 지연로딩을 사용하는 것을 권장한다고 한다.
▷주의할 것.
엔티티 간의 연관관계를 지정하는 경우엔 항상 @ToString()을 주의해야 한다고 한다.
@ToStirng()은 해당 클래스의 모든 멤버 변수를 출력하는데, 연관관계 매핑이 되어있을 경우 그 객체 역시 출력해야 하기 때문에 이때 데이터베이스 연결이 필요하게 된다.
이런 문제로 인해 연관관계가 있는 엔티티 클래스의 경우 @ToString()할 땐 습관적으로 exclude 속성을 사용하는 것이 좋은데 exclude는 해당 속성값으로 지정된 필드는 toString()에서 제외해준다.
▷주의할 것 두번 째 : N+1문제
N + 1 문제는 JPA 를 사용한다면 굉장히 흔하게 접할 수 있는 문제이기 때문에 더욱 중요하다.
역으로 Team이 연관관계의 주인으로 OneToMany를 갖는 예시를 통해 알아보겠다.
@Entity
public class Memeber {
@Id @GeneratedValue
private Long id;
private String userName;
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private teamName;
@OneToMany
@JoniColumn(name = "member_id")
private List<Member> members;
}
만약 위의 사례에서 member가 12명이라고 가정한다면 TeamA를 find()하면 아래와 같은 쿼리문이 도출된다.
SELECT * FROM Team WHERE teamId = 1; // TeamA 의 teamId 는 1 이라고 가정한다.
SELECT * FROM Member WHERE memberId = "member1";
SELECT * FROM Member WHERE memberId = "member2";
SELECT * FROM Member WHERE memberId = "member3";
SELECT * FROM Member WHERE memberId = "member4";
SELECT * FROM Member WHERE memberId = "member5";
SELECT * FROM Member WHERE memberId = "member6";
SELECT * FROM Member WHERE memberId = "member7";
SELECT * FROM Member WHERE memberId = "member8";
SELECT * FROM Member WHERE memberId = "member9";
SELECT * FROM Member WHERE memberId = "member10";
SELECT * FROM Member WHERE memberId = "member11";
SELECT * FROM Member WHERE memberId = "member12";
이처럼 한번의 요청에 수많은 쿼리가 발생하는 경우를 n+1 문제라고 하고, 이 문제가 발생할때 트래픽이 증가함에 따라 SQL 쿼리의 수는 기하급수적으로 늘어나게 될 것이다. (ex. 10만번의 요청이 들어올경우 등)
n+1문제는 서비스에 심각한 성능저하를 유발할 수 있기때문에 Fetch Join, @EntityGraph 등을 이용하여 해결하여야 한다.
● 연관관계를 설정해 댓글 구현하기
앞에서 살펴본 연관관계를 보고 각 엔티티간에 애노테이션을 활용해 연관관계를 설정하는 방법은 알게 되었으니 스터디에서 하기로 한 댓글창 구현을 어떤식으로 할지 예시로 보겠다.개발 진행 순서는 comment > post(board) > repository > DTO(Entity) ( CommentRequestDto > CommentResponseDto > PostsResponseDto) > controler(PostsIndexController > CommentApiController) > service(CommentService) 으로 6단계이다.
▷Comment
uilder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Table(name = "comments")
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(columnDefinition = "TEXT", nullable = false)
private String comment; // 댓글 내용
@Column(name = "created_date")
@CreatedDate
private String createdDate;
@Column(name = "modified_date")
@LastModifiedDate
private String modifiedDate;
@ManyToOne
@JoinColumn(name = "posts_id")
private Posts posts;
@ManyToOne
@JoinColumn(name = "user_id")
private User user; // 작성자
}
이 예시는 댓글 테이블 엔티티로 아래에 post (게시글), 사용자 와 ManyToOne관계의 주인이 된것을 볼 수 있다.
한 개의 게시글에 여러개의 댓글이 달리고, 한 명의 사용자가 여러 개의 댓글을 달 수 있기 때문.
▷Posts(우리 게시판 스터디에선 Board)
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Entity
public class Posts extends TimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 500, nullable = false)
private String title;
@Column(columnDefinition = "TEXT", nullable = false)
private String content;
private String writer;
@Column(columnDefinition = "integer default 0")
private int view;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "posts", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
@OrderBy("id asc") // 댓글 정렬
private List<Comment> comments;
/* 게시글 수정 메소드 */
public void update(String title, String content) {
this.title = title;
this.content = content;
}
}
이후 게시글 엔티티를 보면, 사용자에겐 @ManyToOne 과 댓글에겐 @OneToMany 로 양방향 관계를 맺어준것을 볼 수 있다.
그리고 게시글이 삭제되면 댓글 또한 삭제되어야 하기 때문에 CascadeType.REMOVE 속성을 사용했고, @OrderBy 어노테이션을 이용하여 간단히 정렬 처리를 했다.
이후 내용은 https://dev-coco.tistory.com/132#--%--Service 를 참고하면된다.
참조 :
ManyToOne > https://dev-coco.tistory.com/106
'TIL' 카테고리의 다른 글
| 2023.01.05 TIL (0) | 2023.01.05 |
|---|---|
| 2023.01.04 TIL (0) | 2023.01.04 |
| 2023.01.02 TIL (0) | 2023.01.02 |
| 2022.12.29 TIL (0) | 2022.12.29 |
| 2022.12.28 TIL (0) | 2022.12.28 |