이번주 인상깊은 개념에 대해 알게 됐다.
"A 객체가 B클래스의 객체를 포함하기"
Case 1
항상 아래 사진처럼 Author(저자) 테이블에는 id, name, email, password
Post(게시글) 테이블에는 id, title, contents, author_id(Author 테이블의 id를 받음) 변수들을 두고 관리했었는데
이렇게 설정하게 되면 저자가 Post를 작성할 때 자신의 고유한 id를 알고 있어야 한다는 문제가 발생한다.
(여기서 id는 회원가입 시 아이디-비밀번호의 아이디가 아닌, 데이터베이스 상에 저장되는 회원가입 순서의 고유번호를 말한다)
package C12ClassLecture;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class AuthorPostService {
public static void main(String[] args) {
List<Author> authors = new ArrayList<>();
List<Post> posts = new ArrayList<>();
while(true){
Scanner sc = new Scanner(System.in);
System.out.println("1번:회원가입, 2번:게시글작성, 3번:회원목록조회, 4번:회원상세조회, 5번:게시글상세조회 ");
int number = Integer.parseInt(sc.nextLine());
switch (number){
case 1:
System.out.println("회원가입하실 이름을 입력해주세요");
String name = sc.nextLine();
System.out.println("회원가입하실 이메일을 입력해주세요");
String email = sc.nextLine();
Author author1 = new Author(name, email);
authors.add(author1);
break;
case 2:
System.out.println("작성자 id를 입력해주세요");
Long id = Long.parseLong(sc.nextLine());
System.out.println("게시글명을 입력해주세요");
String title = sc.nextLine();
System.out.println("게시글 내용을 입력해주세요");
String contents = sc.nextLine();
Post post1 = new Post(title, contents, id);
posts.add(post1);
break;
// 회원목록조회
case 3:
for(Author a : authors){
System.out.println(a.getEmail());
}
break;
// 회원상세조회 : 이름, email, 작성글수
case 4:
System.out.println("author email를 입력해주세요");
String author_email = sc.nextLine();
Author temp_author = null;
for(Author a : authors){
if(a.getId().equals(author_email)){
temp_author = a;
break;
}
}
int count = 0;
for(Post p : posts){
if(p.getAuthor_id() == temp_author.getId()){
count++;
}
}
System.out.println(temp_author.getName() + " " + temp_author.getEmail() + " " + count);
break;
// 게시글상세조회
case 5:
System.out.println("post id를 입력해주세요");
Long post_id = Long.parseLong(sc.nextLine());
Post temp_post = null;
for(int i=0; i<posts.size(); i++){
if(posts.get(i).getId() == post_id){
temp_post = posts.get(i);
break;
}
}
Author temp_post_author = null;
for(Author a : authors){
if(a.getId() == temp_post.getAuthor_id()){
temp_post_author = a;
}
}
System.out.println(temp_post.getTitle() + " " + temp_post_author.getEmail());
break;
}
}
}
}
//엔티티
class Author{
private Long id;
private String name;
private String email;
static Long static_id = 0L;
Author(String name, String email){
static_id += 1;
this.id = static_id;
this.name = name;
this.email = email;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
}
class Post{
private Long id;
private String title;
private String contents;
private Long author_id;
static Long static_id = 0L;
Post(String title, String contents, Long author_id){
static_id += 1;
this.id = static_id;
this.title = title;
this.contents = contents;
this.author_id = author_id;
}
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
public String getContents() {
return contents;
}
public Long getAuthor_id() {
return author_id;
}
}
그러니까 위처럼 회원가입을 완료했는데, 게시글을 작성하려고 하니
Post에 작성자의 id를 받아 author_id에 넣어줘야 하기 때문에 작성자 id를 입력받을 수 밖에 없는 상황이 발생한다는 것이다.
사용자가 자신이 몇 번째로 가입됐는지 그 고유번호를 어떻게 안단 말인가!!
이런 서비스 있었으면 당장에 삭제해버렸을 것 같다
Case 2 - Post 클래스에 Author 객체를 포함하도록 수정(Mandatory)
그래서, 대안은 서로가 (이 경우에는 특히 Post에 Author 객체를) 서로의 객체를 갖고 있는 것이다.
Post 객체에 Author 객체를 인스턴스 변수로 넣어줌으로써,
게시글이 생성될 때마다 어떤 저자가 작성했는지의 정보가 꼬리표처럼 같이 줄줄 따라다니게 만드는 것이다.
이렇게 만들었을 때의 장점 !
Case 1의 경우 "게시글 상세조회" 기능에서 사용자의 post id(이것도 웃김. 사용자가 자기가 쓴 글의 고유 id를 어떻게 알아?)를 입력 받아 (1) 해당 post가 어떤 객체인지 찾고,
(2) 찾은 post 객체에 들어있는 author_id 값을 꺼내
(3) 어떤 저자가 글을 작성했는지 조회하는
3단계의 과정을 거쳐야 하는 문제가 있었다.
// 게시글상세조회
case 5:
System.out.println("post id를 입력해주세요");
Long post_id = Long.parseLong(sc.nextLine());
Post temp_post = null;
for(int i=0; i<posts.size(); i++){
if(posts.get(i).getId() == post_id){
temp_post = posts.get(i);
break;
}
}
Author temp_post_author = null;
for(Author a : authors){
if(a.getId() == temp_post.getAuthor_id()){
temp_post_author = a;
}
}
System.out.println(temp_post.getTitle() + " " + temp_post_author.getEmail());
break;
Case 2처럼 구조를 개선하게 된다면 post id(알고 있다고 가정)를 입력할 경우,
(1) 해당 post가 어떤 객체인지 찾고,
(2) post 객체 안에 있는 Author 객체를 통해 한 번에 어떤 저자가 글을 작성했는지 알 수 있게 된다.
// 게시글상세조회
case 5:
System.out.println("post id를 입력해주세요");
Long post_id = Long.parseLong(sc.nextLine());
Post temp_post = null;
for(int i=0; i<posts.size(); i++){
if(posts.get(i).getId() == post_id){
temp_post = posts.get(i);
break;
}
}
//post에 저자 객체 Author가 붙어 있어 쉽게 조회 가능
System.out.println(temp_post.getTitle() + " " + temp_post.getAuthor().getEmail());
break;
아래 좌라락 붙어있던 코드들이 없어지면서, 훨씬 더 간결하고 좋은 코드가 됐다.
스프링에서는 보통 이런식으로 많이 동작한다고 하는데,
다른 객체를 다른 객체에서 갖고 있으면 위처럼 특정 값을 조회할 때 효율적으로 찾아올 수 있다.
package C12ClassLecture;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class AuthorPostService {
public static void main(String[] args) {
List<Author> authors = new ArrayList<>();
List<Post> posts = new ArrayList<>();
while(true){
Scanner sc = new Scanner(System.in);
System.out.println("1번:회원가입, 2번:게시글작성, 3번:회원목록조회, 4번:회원상세조회, 5번:게시글상세조회 ");
int number = Integer.parseInt(sc.nextLine());
switch (number){
case 1:
System.out.println("회원가입하실 이름을 입력해주세요");
String name = sc.nextLine();
System.out.println("회원가입하실 이메일을 입력해주세요");
String email = sc.nextLine();
Author author1 = new Author(name, email);
authors.add(author1);
break;
case 2:
System.out.println("이메일을 입력해주세요");
String author_email = sc.nextLine();
// Author 객체 찾기
Author writer = null;
for(Author a : authors){
if(a.getEmail().equals(author_email)) {
writer = a;
break;
}
}
if(writer==null){
System.out.println("없는 사용자입니다");
continue;
}
System.out.println("게시글명을 입력해주세요");
String title = sc.nextLine();
System.out.println("게시글 내용을 입력해주세요");
String contents = sc.nextLine();
Post post1 = new Post(writer, title, contents);
posts.add(post1);
break;
// 회원목록조회
case 3:
for(Author a : authors){
System.out.println(a.getEmail());
}
break;
// 회원상세조회 : 이름, email, 작성글수
case 4:
System.out.println("author email을 입력해주세요");
String author_email2 = sc.nextLine();
Author author = null;
for(Author a : authors){
if(a.getEmail().equals(author_email2)) {
author = a;
break;
}
}
int count = 0;
for(Post p : posts){
if(p.getAuthor() == author) {
count++;
}
}
System.out.println("회원명 : " + author.getName() +
"\n회원이메일 : " + author.getEmail() +
"\n글 작성수" + count);
break;
// 게시글상세조회
case 5:
System.out.println("post id를 입력해주세요");
Long post_id = Long.parseLong(sc.nextLine());
Post temp_post = null;
for(int i=0; i<posts.size(); i++){
if(posts.get(i).getId() == post_id){
temp_post = posts.get(i);
break;
}
}
//post에 저자 객체 Author가 붙어 있어 쉽게 조회 가능
System.out.println(temp_post.getTitle() + " " + temp_post.getAuthor().getEmail());
break;
// Author temp_post_author = null;
// for(Author a : authors){
// if(a.getId() == temp_post.getAuthor_id()){
// temp_post_author = a;
// }
// }
}
}
}
}
//엔티티
class Author{
private Long id;
private String name;
private String email;
//private List<Post> posts;
static Long static_id = 0L;
Author(String name, String email){
static_id += 1;
this.id = static_id;
this.name = name;
this.email = email;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
}
class Post{
private Long id;
private String title;
private String contents;
private Author author;
static Long static_id = 0L;
Post(Author author, String title, String contents){
static_id += 1;
this.id = static_id;
this.author = author;
this.title = title;
this.contents = contents;
}
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
public String getContents() {
return contents;
}
public Author getAuthor() {
return author;
}
}
Case 3 - Author 클래스에 Post 객체를 포함하도록 수정(Optional)
추가적으로, Author에도 List<Post>를 포함해주면서
저자가 글을 작성할 때마다, 지금까지 어떤 글들을 썼는지의 정보를 Author 객체에 저장한다.
추후 특정 저자가 작성한 글들을 조회하거나, 몇 개의 글을 작성했는지 등의 정보를 찾을 때 유용하게 사용될 수 있다.
package C12ClassLecture;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class AuthorPostService {
public static void main(String[] args) {
List<Author> authors = new ArrayList<>();
List<Post> posts = new ArrayList<>();
while(true){
Scanner sc = new Scanner(System.in);
System.out.println("1번:회원가입, 2번:게시글작성, 3번:회원목록조회, 4번:회원상세조회, 5번:게시글상세조회 ");
int number = Integer.parseInt(sc.nextLine());
switch (number){
case 1:
System.out.println("회원가입하실 이름을 입력해주세요");
String name = sc.nextLine();
System.out.println("회원가입하실 이메일을 입력해주세요");
String email = sc.nextLine();
Author author1 = new Author(name, email);
authors.add(author1);
break;
case 2:
System.out.println("이메일을 입력해주세요");
String author_email = sc.nextLine();
// Author 객체 찾기
Author writer = null;
for(Author a : authors){
if(a.getEmail().equals(author_email)) {
writer = a;
break;
}
}
if(writer==null){
System.out.println("없는 사용자입니다");
continue;
}
System.out.println("게시글명을 입력해주세요");
String title = sc.nextLine();
System.out.println("게시글 내용을 입력해주세요");
String contents = sc.nextLine();
Post post1 = new Post(writer, title, contents);
posts.add(post1);
// writer.getPosts().add(post1); -> 기능은 맞으나 가독성이 떨어짐
// 의미에 맞는 addPost함수 추가 후 사용
writer.addPost(post1);
// ** 아이디어 **
// addPost를 생성자에 합친 코드 사용 한다면
// posts.add(post1);에서 List<Post> posts에 생성과 동시에 post를 넣어주는 과정 수행.
// writer.addPost(post1);없이 posts.add(post1); 만 작성해줘도 동작함
break;
// 회원목록조회
case 3:
for(Author a : authors){
System.out.println(a.getEmail());
}
break;
// 회원상세조회 : 이름, email, 작성글수
case 4:
System.out.println("author email을 입력해주세요");
String author_email2 = sc.nextLine();
Author author = null;
for(Author a : authors){
if(a.getEmail().equals(author_email2)) {
author = a;
break;
}
}
System.out.println("회원명 : " + author.getName() +
"\n회원이메일 : " + author.getEmail() +
"\n글 작성수" + author.getPosts().size());
break;
// 게시글상세조회
case 5:
System.out.println("post id를 입력해주세요");
Long post_id = Long.parseLong(sc.nextLine());
Post temp_post = null;
for(int i=0; i<posts.size(); i++){
if(posts.get(i).getId() == post_id){
temp_post = posts.get(i);
break;
}
}
//post에 저자 객체 Author가 붙어 있어 쉽게 조회 가능
System.out.println(temp_post.getTitle() + " " + temp_post.getAuthor().getEmail());
break;
}
}
}
}
//엔티티
class Author{
private Long id;
private String name;
private String email;
private List<Post> posts;
static Long static_id = 0L;
Author(String name, String email){
static_id += 1;
this.id = static_id;
this.name = name;
this.email = email;
this.posts = new ArrayList<>(); //일반적으로 생성자 호출 시 초기화
}
void addPost(Post post){
this.posts.add(post);
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public List<Post> getPosts() {
return posts;
}
}
class Post{
private Long id;
private String title;
private String contents;
private Author author;
static Long static_id = 0L;
Post(Author author, String title, String contents){
static_id += 1;
this.id = static_id;
this.author = author;
this.title = title;
this.contents = contents;
}
// ** 아이디어 **
// addPost를 생성자로 합치기 -> 자기 자신을 지칭하는 키워드 this 활용
// addPost(주소) 저장되기 때문에 먼저 생성해두고 나중에 값을 넣어줘도 영향을 받지 않음.
// Post(Author author, String title, String contents){
// static_id += 1;
// this.id = static_id;
// this.author = author;
// this.author.addPost(this);
// this.title = title;
// this.contents = contents;
// }
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
public String getContents() {
return contents;
}
public Author getAuthor() {
return author;
}
}
***여기서 간단한 이슈가 있었다.
2번 기능에서 보통은 게시글이 작성되면, post 객체들을 모아놓는 리스트인 posts에 생성한 post 객체를 추가하고,
글을 쓴 저자 객체가 가진 List<Post>에도 해당 게시글을 추가해준다. (addPost함수)
Post(Author author, String title, String contents){
static_id += 1;
this.id = static_id;
this.author = author;
this.title = title;
this.contents = contents;
}
void addPost(Post post){
this.posts.add(post);
}
case 2:
System.out.println("이메일을 입력해주세요");
String author_email = sc.nextLine();
// Author 객체 찾기
Author writer = null;
for(Author a : authors){
if(a.getEmail().equals(author_email)) {
writer = a;
break;
}
}
if(writer==null){
System.out.println("없는 사용자입니다");
continue;
}
System.out.println("게시글명을 입력해주세요");
String title = sc.nextLine();
System.out.println("게시글 내용을 입력해주세요");
String contents = sc.nextLine();
Post post1 = new Post(writer, title, contents);
posts.add(post1);
writer.addPost(post1);
break;
post 객체가 생성될 때마다, 작성자에게 일일이 addPost 해주는 것이 귀찮다.
그러면 post 객체가 생성될 때, 생성자에서 동시에 작성한 author 객체에 addPost해줄 수 있을까?
Post 생성자를 아래와 같이 수정해봤다. (자기 자신을 지칭하는 키워드 this 활용해서 addPost를 생성자에 합침)
Post(Author author, String title, String contents){
static_id += 1;
this.id = static_id;
this.author = author;
this.author.addPost(this);
this.title = title;
this.contents = contents;
}
Post과 동시에 author 객체에 현재 생성하고 있는 Post 객체를 넣어주는 작업이다보니
Post 생성이 끝나기도 전에 객체에도 넣어주는 느낌이라 불가능할 것 같았는데 잘 작동된다.
addPost() 매개변수로 주소값이 넘어가기 때문에 먼저 생성만해두고 나중에 값을 넣어줘도 영향을 받지 않는 것이다.
Post post1 = new Post(writer, title, contents);
posts.add(post1);
생성자를 이렇게 수정할 경우에는 post 생성자에서 author 객체에 post 정보를 붙이는 역할까지 하게되니까
writer.addPost(post1); 코드를 삭제할 수 있게된다.
스프링을 얕게 배웠음에도 불구하고, 진짜 스프링의 본질에 대해서는 잘 몰랐던 것 같다.
이번 내용을 정리하면서 "객체가 객체를 가지고 있으면, 해당 객체에서 다른 객체의 값들을 다룰 때 효율적이다"라는 것을 알게 되었고
나중에 배울 스프링에서 더 잘 이해할 수 있을 것이라는 생각이 든다.
'Language > JAVA' 카테고리의 다른 글
[JAVA] Comparator와 Comparable 이해하기 (1) | 2023.12.22 |
---|---|
[JAVA] 인터페이스 개념과 장점 쉬운 예시로 이해하기 (3) | 2023.12.21 |
[JAVA] 자바 그래프 최단거리(BFS) (0) | 2023.03.29 |
[JAVA] 자바 그래프 경로탐색 DFS 인접행렬, 인접리스트 (0) | 2023.03.27 |
[JAVA] 자바 그래프와 인접행렬 (0) | 2023.03.26 |