학생이 수강신청을 하는 DB를 예시로 들어보겠다.

 

한 명의 student는 여러 개의 lecture에 들어갈 수 있고 

마찬가지로 하나의 lecture에도 여러 student가 포함될 수 있는 N:N관계의 DB다.

위와 같이 N : N 관계의 테이블 설정에서는 @ManyToMany 를 아래와 같이 설정할 수 있다.

 

@Entity
@Getter @Setter
public class Student {

    @Id
    @Column(name = "student_id")
    private Long id;

    private String name;

    @ManyToMany
    @JoinTable(name = "student_lecture"
            ,joinColumns = @JoinColumn(name = "student_id")
            ,inverseJoinColumns = @JoinColumn(name = "lecture_id"))
    private List<Lecture> lectures = new ArrayList<>();
}

 

@Entity
@Getter @Setter
public class Lecture {
    @Id
    @Column(name = "lecture_id")
    private Long id;

    @ManyToMany(mappedBy = "lectures")
    private List<Student> students = new ArrayList<>();
}

 

 

하지만 문제는 실제 테이블을 작성하다보면

student_lecture 테이블에 create_time, update_time 등의 다른 칼럼들이 추가될 일은 많은데

@ManyToMany로 Entity의 변수를 매핑하다 보면 student_lecture 테이블에 다른 column을 추가할 방법이 없다는 것이다.

 

그러한 이유로 아래와 같이 별도의 StudentLecture 클래스를 두고 @OneToMany , @ManyToOne을 사용하여 매핑하는 것을 추천한다.

@Entity
@Getter @Setter
public class Student {

    @Id
    @Column(name = "student_id")
    private Long id;

    private String name;

    @OneToMany(mappedBy = "student")
    private List<StudentLecture> studentLectures = new ArrayList<>();
}
@Entity
@Getter @Setter
public class Lecture {
    @Id
    @Column(name = "lecture_id")
    private Long id;

    @OneToMany(mappedBy = "lecture")
    private List<StudentLecture> studentLectures = new ArrayList<>();
}
@Entity
@Getter @Setter
public class StudentLecture {

    @Id
    @Column(name = "student_lecture_id")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "student_id")
    private Student student;

    @ManyToOne
    @JoinColumn(name = "lecture_id")
    private Lecture lecture;
    
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

위와 같이 @ManyToMany 대신 @OneToMany, @ManyToOne 을 사용하고

다대다 관계를 매핑할수 있는 클래스를 사용하면 column을 추가하는 것이 가능해진다.

 

 

틀린 부분은 지적해주시면 언제나 감사히 수정반영 하겠습니다.😄

System.out.println 이 아닌 SLF4J, Logback 등을 이용한 logger를 사용해야 하는 이유에 대해 알아보자

 

대충 요약 : 그냥 System.out.println 쓰지 말고 logger를 써라

 

 

3줄 요약 

System.out은 속도가 느리다.

System.out은 로깅 레벨을 조절할 수 없다.

logger 는 별도의 기능들을 더 제공한다.

 

자세히.

1. 속도

System.out 은 내부 버퍼링과, 멀티 쓰레드 등의 환경에 따라 로그를 모아서 출력하는 등의 상황이 발생한다. 물론 logger 또한 내부 버퍼링이 있지만 성능 최적화가 조금 더 잘 되어있다 

(자세히 : 링크 TODO )

System.out.println("sysout");
System.err.println("syserr");

logger.trace("LOG TRACE");
logger.debug("LOG DEBUG");
logger.info( "LOG INFO");
logger.warn( "LOG WARN");
logger.error("LOG ERROR");

 

2. 로그 레벨 조절

System.out.println (혹은 System.err.println) 은 로그 레벨을 조절할 수 없는 반면

logger는 기본적으로 5개의 로그 레벨을 지원하므로 필요에 따라 다양하게 쓸 수 있으며 이 로그 레벨을 별도의 어플리케이션 코드를 바꾸지 않고 외부에서 설정을 바꿔서 로그 레벨 조절이 가능하다. 이를 활용하여 특정 상황에 맞는 로그를 출력할 수 있다.

 

3. 로그 파일 관리

추가로 수많은 로그가 남는 시스템에서는 로그 파일 크기로 인해 서비스 장애가 발생할 수 있는데

logger에서는 날짜별 로그파일 분리, 파일 크기별 로파일 분리 등을 지원한다.

 

 

 

logger 사용의 올바른 예시

아래의 코드에서 위 두줄과 아래 코드 방식마다 결과의 차이가 있을까? 결론부터 말하자면 "차이가 있을 수 있다." 이다.

String name = "Chris";
String id = "chris1108";

// 1. noncompliant
logger.info("name = " + name);
logger.debug("id = " + id);

// 2. compliant 
if ( logger.isInfoEnabled() ) {
	logger.info("name = " + name);
}
if ( logger.isDebugEnabled() ){
	logger.debug("id = " + id);
}

// 3. compliant 
logger.info("name = {}", name);
logger.debug("id = {}", id);

먼저 1번을 보자

로그 레벨이 DEBUG 일 경우에는 문제가 발생하지 않고 DEBUG 보다 높은 INFO, WARN, ERROR 일 경우에 문제가 발생한다.

java의 연산 순서상 logger.debug 함수로 String을 전달하기 전에 "id = " + id 연산을 처리하게 된다.

log 내부에서는 로그 레벨이 INFO 이기 때문에 별다른 로그 출력을 하지 않게 되겠지만 결과적으로 "id = " 와 id를 더하는 연산은 쓸모없는 냥비가 되는 셈이다.

 

그래서 아래의 2,3번 방법을 사용할 수 있다.

로그 레벨이 DEBUG 일 경우에만 "id = " + id 연산을 하게 하여 불필요한 연산을 줄일 수 있다.

 

여기서 더 나아가 3번의 방법을 사용하면

logger 내부에 "id = {}" 와 id 두 개의 String 을 parameter로 전달하고 내부에서 필요할 경우에만 연산을 처리해 주고 코드의 가독성 또한 높일 수 있다.

@@
예상 질문 질의응답 모음
Java 면접
Spring 면접

■ 객체지향이란? 

프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고 그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법입니다

 

■ 객체지향 프로그래밍의 장점

코드 재사용이 용이 => 남이 만든 클래스를 가져와서 사용가능
유지보수가 쉬움 => 절차지향 프로그래밍은 일일이 찾아 수정해야하는 반면에 해당하는 부분만 수정하면됨
대형 프로젝트에 적합 => 클래스 단위로 모듈화 시켜서 개발가능

 

■ 객체 지향적 설계 원칙

SRP(Single Responsibility Principle) : 단일 책임 원칙 클래스는 단 하나의 책임을 가져야 하며 클래스를 변경하는 이유는 단 하나의 이유이어야 한다.
OCP(Open-Closed Principle) : 개방-폐쇄 원칙 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.
LSP(Liskov Substitution Principle) : 리스코프 치환 원칙 상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.
ISP(Interface Segregation Principle) : 인터페이스 분리 원칙 인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.
DIP(Dependency Inversion Prinsiple) : 의존 역전 원칙 고수준 모듈은 저수준 모듈의 구현에 의존해서는 안된다.

 

■ 객체지향 프로그래밍 키워드

추상화 : 불필요한 정보는 숨기고 중요한 정보만을 표현함으로써 공통의 속성이나 기능을 묶어 이름을 붙이는 것
캡슐화 : 기능과 특성의 모음을 "클래스"라는 "캡슐"에 넣어서 분류해서 넣는 이 캡슐화다.
상속 :상속은 부모클래스의 속성과 기능을 그대로 이어받아 사용할 수 있게하고 기능의 일부분을 변경해야 할 경우 상속받은 자식클래스에서 해당 기능만 다시 수정(정의)하여 사용할 수 있게 하는 것이다. 다중 상속은 불가하다.
다형성 : 하나의 변수명, 함수명 등이 상황에 따라 다른 의미로 해석될 수 있는 것이다.즉, 오버라이딩(Overriding), 오버로딩(Overloading)이 가능하다는 얘기다.
오버라이딩 : 부모클래스의 메서드와 같은 이름, 매개변수를 재정의 하는 것.
오버로딩 : 같은 이름의 함수를 여러 개 정의하고, 매개변수의 타입과 개수를 다르게 하여 매개변수에 따라 다르게 호출할 수 있게 하는 것.

 

■ 제네릭

제네릭(Generic)은 클래스 내부에서 지정하는 것이 아닌 외부에서 사용자에 의해 지정되는 것을 의미한다.

■ 컬렉션 클래스에서 제네릭을 사용하는 이유를 설명하세요

컬렉션 클래스에 저장되는 인스턴스 타입을 제한하여 런타임에 발생할 수 있는 잠재적인 모든 예외를 컴파일 타임에 잡아 낼 수 있어서 사용합니다.

 

■ 데드락 개념, 해결방법

둘 이상의 스레드가 lock을 획득하기 위해 기다리는데, 이 lock을 잡고 있는 스레드도 똑같이 다른 lock을 기다리면서 서로 블락상태에 놓이는 것을 말한다
우선순위를 선정해 자원을 선점하도록 하는 것과 공유 불가능한 상호 배제 조건을 제거하는 것이 있다.

 

■ JVM 역할

자바 컴파일러가 .java 파일을 컴파일 하면, .class라는 자바 바이트코드로 변환시켜줍니다. 이때 바이트 코드가 기계어가 아니기 때문에 운영체제에서 바로 실행을 못하는데 이때 운영체제가 이해할 수 있도록 해석해주는 것이 JVM입니다.
컴파일 -> 바이트 코드 -> 기계어 이런식으로 중간에 바이트 코드 과정이 있기 때문에 속도와 메모리에서 단점이 될 수 있다.
JVM을 사용하면 운영체제에 상관없이 같은 코드를 사용할 수 있습니다.

 

■ Object

객체(Object)란, 소프트웨어 세계에 구현할 대상
클래스의 인스턴스 라고도 부른다. OOP 관점에서 클래스의 타입으로 선언되었을 때 객체라고 부른다.
객체는 현실 세계에 가깝고, 인스턴스는 소프트웨어 세계에 가깝다.

 

■ 클래스

연관되어 있는 변수와 메서드의 집합
클래스란, 객체를 만들어 내기 위한 설계도 혹은 틀

 

■ 인스턴스

객체를 소프트웨어에 실체화 하면 그것을 인스턴스 라고 부른다.
OOP의 관점에서 객체가 메모리에 할당되어 실제 사용될 때 인스턴스 라고 부른다.
객체는 클래스의 인스턴스이다.

 

■ 인스턴스화

클래스로부터 객체를 만드는 과정을 클래스의 인스턴스화(instantiate) 라고 한다.
어떤 클래스로부터 만들어진 객체를 그 클래스의 인스턴스(instance) 라고 한다.

 

■ String, StringBuffer, StringBuilder 차이점이 무엇인가요

첫번째 차이점은 String은 불변하다는 특징을 가지고 있어서 수정을 하지못하고 새로운 String 인스턴스가 생성되고 전에 있던 String은 GC에 의해 사라지게 된다. 그래서 좋은 성능을 기대하기는 힘들다. (String 불변, StringBuffer, StringBuilder 가변)
StringBuffer는 동기화 키워드를 지원하여 멀티쓰레드 환경에서 안전하다는 점(thread-safe) 입니다. 참고로 String도 불변성을 가지기때문에 마찬가지로 멀티쓰레드 환경에서의 안정성(thread-safe)을 가지고 있습니다.
반대로 StringBuilder는 동기화를 지원하지 않기때문에 멀티쓰레드 환경에서 사용하는 것은 적합하지 않지만 동기화를 고려하지 않는 만큼 단일쓰레드에서의 성능은 StringBuffer 보다 뛰어납니다

 

■ 프로세스란?

운영체제로부터 시스템 자원을 할당받는 작업의 단위
하나의 프로세스는 크게 코드영역(code), 데이터 영역(date), 스택 영역(stack), 힙 영역(heap) 4가지로 이루어져 있습니다.

■ 스레드란?

한 프로세스 내에서 동작되는 여러 실행의 흐름, 프로세스 하나에 자원을 공유하면서 일련의 과정을 여러 개를 동시에 실행 시킬 수 있다.
멀티프로세스
장점 : 안정성이 높다 (독립된 구조기 때문에)
단점 : 여러 프로세스를 왔다갔다 하는 컨텍스트 스위칭으로 인한 성능저하
멀티쓰레드
장점 : 응답시간 단축, 자원소모 감소
단점 : 미묘한 시간차나 변수 공유함으로써 오류 발생 가능

■ 자바 람다 관련 함수형 프로그래밍

함수형 프로그래밍은 명령형이 아닌 선언적 방식으로 구현되며 흐름 제어를 명시적으로 기술하지 않고 프로그램 로직이 표현된다는 것을 의미한다.
람다는 함수의 구조로 되어있고 -> 와 같이 화살표 형태의 기호를 이용해 매개변수를 함수 바디로 전달하는 형태

@@ TODO 명령형 vs 선언적 방식의 구현

■ 가비지컬렉션이란?

C/C++ 언어와 달리 자바는 개발자가 명시적으로 객체를 해제할 필요가 없습니다. 자바 언어의 큰 장점이기도 합니다. 사용하지 않는 객체는 메모리에서 삭제하는 작업을 Gargabe Collection(GC)라고 부르며 JVM에서 GC를 수행합니다.

■ 자바 컬렉션 List, set, map에 대한 설명

List : 순서가 있는 데이터의 집합으로 데이터의 중복을 허용한다.
Set : 순서를 유지하지 않는 데이터의 집합으로 데이터의 중복을 허용하지 않는다.
Map : 키, 값으로 이루어진 데이터의 집합으로, 순서는 유지되지 않으며 키의 중복을 허용하지 않으나 값의 중복은 허용한다.

■ 테스트 코드에 대한 설명??

단위테스트를 사용하면 좋은점은 개발 초기에 문제를 발견할 수 있다.

'Java' 카테고리의 다른 글

logger를 사용하자  (0) 2023.02.16
wildfly jboss 서버 아이디 이름 확인  (0) 2022.01.20
Spring Batch embedded tomcat  (0) 2020.07.28
java.lang.NoClassDefFoundError: sun/misc/BASE64Encoder  (0) 2020.01.28
Map 선언 초기화 동시에  (0) 2019.10.24

wildfly에서 jboss를 통해 하나의 deployment 를 여러 서버에 올렸을 경우 java 에서 어느 서버인지 확인하는 법

String result1 = System.getProperty("jboss.node.name");
String result2 = System.getProperty("jboss.server.name");
System.out.println(result1);
System.out.println(result2);

결과

hostName1:server01-1
server01-1

 

'Java' 카테고리의 다른 글

logger를 사용하자  (0) 2023.02.16
Java 면접 예상질문 질의응답  (0) 2023.01.31
Spring Batch embedded tomcat  (0) 2020.07.28
java.lang.NoClassDefFoundError: sun/misc/BASE64Encoder  (0) 2020.01.28
Map 선언 초기화 동시에  (0) 2019.10.24

 

2020-07-28 14:41:25.412 [restartedMain] INFO  o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat initialized with port(s): 8080 (http)
2020-07-28 14:41:25.438 [restartedMain] INFO  org.apache.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"]

spring batch 돌릴때 내장 톰캣이 기본으로 실행되는데 해당 톰캣 끄는 방법

 

spring.main.web-application-type=none

 

 

 

 

Caused by: java.lang.NoClassDefFoundError: sun/misc/BASE64Encoder 

 

 

Java 9 부터는 sun.misc.BASE64Encoder 제공하지 않음

 

대신 java.util.Base64 를 사용할것

 

 

 

어쩔 수 없이 사용해야 할 경우 Java 8 이하로 사용

* Java 8 이하로 컴파일 해야할 뿐 아니라 돌리는 환경도 Java 8 이하로 돌려야 함

 

'Java' 카테고리의 다른 글

logger를 사용하자  (0) 2023.02.16
Java 면접 예상질문 질의응답  (0) 2023.01.31
wildfly jboss 서버 아이디 이름 확인  (0) 2022.01.20
Spring Batch embedded tomcat  (0) 2020.07.28
Map 선언 초기화 동시에  (0) 2019.10.24

Map myMap = new HashMap() {{

    put("key1", "value1");

    put("key2", "value2");

}};


 

'Java' 카테고리의 다른 글

logger를 사용하자  (0) 2023.02.16
Java 면접 예상질문 질의응답  (0) 2023.01.31
wildfly jboss 서버 아이디 이름 확인  (0) 2022.01.20
Spring Batch embedded tomcat  (0) 2020.07.28
java.lang.NoClassDefFoundError: sun/misc/BASE64Encoder  (0) 2020.01.28

+ Recent posts