서버에서 client로 여러개의 data를 넘겨 주기 위해 새로운 방법이 필요했다

 

 ErrorCode

@Getter
@AllArgsConstructor
public enum ErrorCode {

    /* 400 BAD_REQUEST : 잘못된 요청 */
    INVALID_REFRESH_TOKEN(BAD_REQUEST, "리프레시 토큰이 유효하지 않습니다"),
    MISMATCH_REFRESH_TOKEN(BAD_REQUEST, "리프레시 토큰의 유저 정보가 일치하지 않습니다"),
    CANNOT_FOLLOW_MYSELF(BAD_REQUEST, "자기 자신은 팔로우 할 수 없습니다"),

    /* 401 UNAUTHORIZED : 인증되지 않은 사용자 */
    INVALID_AUTH_TOKEN(UNAUTHORIZED, "권한 정보가 없는 토큰입니다"),
    UNAUTHORIZED_MEMBER(UNAUTHORIZED, "현재 내 계정 정보가 존재하지 않습니다"),

    /* 404 NOT_FOUND : Resource 를 찾을 수 없음 */
    MEMBER_NOT_FOUND(NOT_FOUND, "해당 유저 정보를 찾을 수 없습니다"),
    REFRESH_TOKEN_NOT_FOUND(NOT_FOUND, "로그아웃 된 사용자입니다"),
    NOT_FOLLOW(NOT_FOUND, "팔로우 중이지 않습니다"),

    /* 409 CONFLICT : Resource 의 현재 상태와 충돌. 보통 중복된 데이터 존재 */
    DUPLICATE_RESOURCE(CONFLICT, "데이터가 이미 존재합니다"),

    ;

    private final HttpStatus httpStatus;
    private final String detail;
}

 

ErrorResponse

@Getter
@Builder
public class ErrorResponse {
    private final LocalDateTime timestamp = LocalDateTime.now();
    private final int status;
    private final String error;
    private final String code;
    private final String message;

    public static ResponseEntity<ErrorResponse> toResponseEntity(ErrorCode errorCode) {
        return ResponseEntity
                .status(errorCode.getHttpStatus())
                .body(ErrorResponse.builder()
                        .status(errorCode.getHttpStatus().value())
                        .error(errorCode.getHttpStatus().name())
                        .code(errorCode.name())
                        .message(errorCode.getDetail())
                        .build()
                );
    }
}
  • 실제로 유저에게 보낼 응답 Format 입니다.
  • 일부러 500 에러 났을 때랑 형식을 맞췄습니다. status, code 값은 사실 없어도 됩니다.
  • ErrorCode 를 받아서 ResponseEntity<ErrorResponse> 로 변환해줍니다.

@ControllerAdvice 는 프로젝트 전역에서 발생하는 모든 예외를 잡아줍니다.

@ExceptionHandler 는 발생한 특정 예외를 잡아서 하나의 메소드에서 공통 처리해줄 수 있게 해줍니다.

따라서 둘을 같이 사용하면 모든 예외를 잡은 후에 Exception 종류별로 메소드를 공통 처리할 수 있습니다.

 

Exception 사용

@RequiredArgsConstructor
@Service
public class MemberService {
    private final MemberRepository memberRepository;

    @Transactional
    public boolean follow(Long memberId) {
        Member currentMember = getCurrentMember();

        // 팔로우할 상대방 정보가 없는 경우
        Member targetMember = memberRepository.findById(memberId)
                .orElseThrow(() -> new CustomException(MEMBER_NOT_FOUND));

        // 자기 자신을 팔로우 하려는 경우
        if (currentMember.equals(targetMember))  {
            throw new CustomException(CANNOT_FOLLOW_MYSELF);
        }

                // code...
    }
}

MEMBER_NOT_FOUND 실제 응답

{
  "timestamp": "2021-03-14T03:29:01.878659",
  "status": 404,
  "error": "NOT_FOUND",
  "code": "MEMBER_NOT_FOUND",
  "message": "해당 유저 정보를 찾을 수 없습니다"
}

 

CANNOT_FOLLOW_MYSELF 실제 응답

{
  "timestamp": "2021-03-14T03:16:25.98361",
  "status": 400,
  "error": "BAD_REQUEST",
  "code": "CANNOT_FOLLOW_MYSELF",
  "message": "자기 자신은 팔로우 할 수 없습니다"
}

결론

Spring 에는 프로젝트 전역에서 발생하는 Exception 을 한 곳에서 처리할 수 있다.

Enum 클래스로 ErrorCode 를 정의하면 Exception 클래스를 매번 생성하지 않아도 된다.

실제 클라에게 날라가는 응답에서 code 부분만 확인하면 어떤 에러가 발생했는지 쉽게 파악 가능하다.

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

항해 99 17일차  (0) 2022.10.05
항해 99 16일차  (0) 2022.10.04
항해 99 13일 차  (0) 2022.10.01
항해 12일 차  (0) 2022.09.30
항해 11일 차  (0) 2022.09.29

알고리즘 공부 및 테스트가 있었고  

금요일 부터 주특기 Spring 본격 시작하였다.

 

  • 기간 9월 23일(금)~29일(목)
  • 알고리즘 문제 해결 및 코딩 테스트 준비, 자바 문법 익숙해지기

 

JVM, JRE, JDK의 차이

자바에서 사용하는 용어 중 혼동하기 쉬운 JVM, JRE, JDK에 대해서 정리해 보자.

JVM

JVM은 자바 가상머신(Java Virtual Machine)의 약자이다.

JVM은 자바 소스코드로부터 만들어지는 자바 바이너리 파일(.class)을 실행할 수 있다. 또한 JVM은 플랫폼에 의존적이다. 즉 리눅스의 JVM과 윈도우즈의 JVM은 다르다. 단, 컴파일된 바이너리 코드는 어떤 JVM에서도 동작시킬 수 있다.

JVM은 다음과 같은 역할을 한다.

  • 바이너리 코드를 읽는다.
  • 바이너리 코드를 검증한다.
  • 바이너리 코드를 실행한다.
  • 실행환경(Runtime Environment)의 규격을 제공한다. (필요한 라이브러리 및 기타파일)

JRE

JRE는 자바 실행환경(Java Runtime Environment)의 약자이다.

JRE는 JVM 이 자바 프로그램을 동작시킬 때 필요한 라이브러리 파일들과 기타 파일들을 가지고 있다. JRE는 JVM의 실행환경을 구현했다고 할 수 있다.

JDK

JDK는 자바 개발도구(Java Development Kit)의 약자이다.

JDK는 JRE + 개발을 위해 필요한 도구(javac, java등)들을 포함한다.

A2 JVM, JRE, JDK의 차이 - 점프 투 자바 (wikidocs.net)

 

메모리

 

 

출처 : http://huelet.tistory.com/entry/JVM-%EB%A9%94%EB%AA%A8%EB%A6%AC%EA%B5%AC%EC%A1%B0

 

메서드 영역

 

메소드 영역은 Class Area, Code Area, Static Area로 불려지며, 의미상 공유 메모리 영역이라고도 불린다. 코드에서 사용되는 클래스들을 클래스 로더로 읽어 클래스 별로 런타임 상수 풀(runtime constant pool), 필드(field) 데이터, 메소드(method) 데이터, 메소드 코드, 생성자 코드 등을 분류해서 저장한다. 메소드 영역은 JVM이 동작해서 클래스가 로딩될때 생성되고, 모든 스레드가 공유하는 영역이다.

 

1. 클래스 내부에서는 직접 접근이 가능하다.(왜? 라고 생각 되면, 위의 내용을 다시 한번...)

public class StaticExample { 
  static int count = 0;

  StaticExample() {}

  public void increase() {
    count += 1;
  }
}

 

 

2. 클래스 이름으로 접근, 인스턴스 생성 후 접근(인스턴스 변수와의 구분이 어렵다. 권장하지 않는다.)

 

public class Main { 
  public static void main(String[] args) {
    StaticExample.count++; //클래스 이름으로 접근

    StaticExample staticExample = new StaticExample();
    staticExample.count++;  //인스턴스 생성 후 접근(권장하지 않음.)
}

 

스텍 영역

 

Stack

  • Heap 영역에 생성된 Object 타입의 데이터의 참조값이 할당된다.
  • 원시타입의 데이터가 값과 함께 할당된다.
  • 지역변수들은 scope 에 따른 visibility 를 가진다.
  • 각 Thread 는 자신만의 stack 을 가진다.

Stack 에는 heap 영역에 생성된 Object 타입의 데이터들에 대한 참조를 위한 값들이 할당된다. 또한, 원시타입(primitive types) - byte, short, int, long, double, float, boolean, char 타입의 데이터들이 할당된다. 이때 원시타입의 데이터들에 대해서는 참조값을 저장하는 것이 아니라 실제 값을 stack 에 직접 저장하게 된다.

 

Heap area(힙 메모리 영역)

 

 인스턴스를 생성하는 방법은 "클래스 변수 =  new 클래스();" 라는 것을 기억하면서 밑에 내용을 확인해 보자.
 참조형(Reference Type)의 데이터 타입을 갖는 객체(인스턴스), 배열 등은 Heap 영역에 데이터가 저장된다. 이때 변수(객체, 객체변수, 참조변수)는 Stack 영역의 공간에서 실제 데이터가 저장된 Heap 영역의 참조값(reference value, 해시코드 / 메모리에 저장된 주소를 연결해주는 값) new 연산자를 통해 리턴 받는다. 다시 말하면 실제 데이터를 갖고 있는 Heap 영역의 참조 값을 Stack 영역의 객체가 갖고 있다이렇게 리턴 받은 참조 값을 갖고 있는 객체를 통해서만 해당 인스턴스를 핸들 할 수 있다.

 
public class HeapAreaEx01 {
    public static void main(String[] args) {
        int[] a = null; // int형 배열 선언 및 Stack 영역 공간 할당
        System.out.println(a); // 결과 : null
        a = new int[5]; // Heap 영역에 5개의 연속된 공간 할당 및 
        // 변수 a에 참조값 할당
        System.out.println(a); // 결과 : @15db9742 (참조값)
    }
}

 설명은 소스 코드의 주석 처리된 부분과 같다. 결국 a변수는 데이터가 저장된 Heap영역의 참조 값을 리턴 받아 갖고 있다는 것이다. 다른 예제도 확인해보자.

 

 
public class HeapAreaEx02 {
    public static void main(String[] args) {
        String str1 = new String("joker");
        String str2 = new String("joker");
        if(str1 == str2){
            System.out.println("같은 주소값 입니다.");
        }else{
            System.out.println("다른 주소값 입니다.");
        }
    }
}

 

 문자열을 저장하는 String도 참조형이다. new 연산자를 이용해서 생성하면 데이터는 Heap 영역에 저장되고 str1과 str2는 참조 값을 리턴 받는다. 저장된 주소가 다르기 때문에 "=="으로 비교 시 "다른 주소값 입니다."가 출력되는 것이다. 마지막으로 예제를 하나 더 살펴보자.

 

 

class A{}

public class HeapArea {
    public static void main(String[] args) {
        A a = null; // A타입의 a객체 선언 및 Stack 영역 공간 할당
        System.out.println(a); // 결과 : null
        a = new A(); // Heap 메모리에 공간 할당 및 객체(a)에 참조값 할당
        System.out.println(a); // 결과 : @15db9742
    }
}

 소스 코드에 주석 처리한 내용으로 설명은 대신하겠다. 결국 객체가 참조 값을 갖는다는 것이다.

 참고로 Heap에 저장된 데이터가 더 이상 사용이 불필요하다면 메모리 관리를 위해 JVM(자바 가상머신)에 의해 알아서 해제된다. 이러한 기능을 가비지컬렉션(GC, 쓰레기 수집)이라고 한다. 
 메모리 관련 내용은 여기서 마무리하겠다. 메모리는 중요한 부분이고 인스턴스 등을 정의할 때 왜 이렇게 사용하는지 이해하는 것이 코딩하는데 있어 많은 도움이 될 것이다. 

배운 것

알고리즘 문제 해결을 통한 구체적인 구현력과 사고력을 향상시킬수 있는 주였고,

JVM 및 세부사항 및 JAVA WEB의 꽃인 SPRING BOOT 가 기본적으로 어떻게 실행 되고 작동되는지 알수 있었다.

 

아쉬웠던 점/느낀점

알고리즘 문제를 풀면서 처음에는 문제를 잘 이해하지 못하고 어떻게 문법을 써야하는지도 잘 알지 못했다.

남들보다 더 많은 시간을 투자해서 공부를 해 주어진 40문제를 풀긴 했지만,  짧은 시간인 만큼 정말 이해가 가지 않는 문제의 경우 그냥 풀의만 보고 해석해보고 정 안되면 그냥 넘겨버린 문제들이 있다.  알고리즘 주를 보내면서 내 실력에 대해 잘 알게 되었고 , 앞으로도 알고리즘 문제는 조금씩이라도 풀어보며 더 익숙해질 것이라고 다짐했다.

또한 사용자의 요구사항을 주어진 기능적으로 구현을 해야하는 것에 대해 조금은 이해를 할 수 있는 시간이엿다.

 

 

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

항해 99 (9기) 7주차 WIL  (0) 2022.11.06
항해 (9기) 6주차 WLI 회고  (0) 2022.10.30
항해 99 주차 4WIL  (0) 2022.10.16
항해 99(9기) 3주차 WTL 회고  (0) 2022.10.09
항해 99 (9기) 1주차 회고  (1) 2022.09.25

어제부터 spring boot 가 시작되었다!

API

  •  👉 클라이언트 - 서버 간의 약속입니다.
  • 클라이언트가 정한대로 서버에게 요청(Request)을 보내면, 서버가 요구사항을 처리하여 응답(Response)을 반환합니다.

REST

  • 👉 REST란, 주소에 명사, 요청 방식에 동사를 사용함으로써 의도를 명확히 드러냄을 의미합니다.

 

[ Lombok의 장점 ]

  • 어노테이션 기반의 코드 자동 생성을 통한 생산성 향상
  • 반복되는 코드 다이어트를 통한 가독성 및 유지보수성 향상
  • Getter, Setter 외에 빌더 패턴이나 로그 생성 등 다양한 방면으로 활용 가능

Lombok을 사용하면 정말 많은 이점을 얻을 수 있다. 과거에는 IntelliJ에서 롬복 플러그인을 모든 팀원이 설치해주어야 한다는 단점도 있었지만 현재는 IntelliJ의 기본 플러그인이 되면서 바로 편리하게 이용가능하게 되었다.

@Getter @Setter ]

Lombok에서 가장 자주 활용하는 어노테이션이다. @Getter와 @Setter를 클래스 이름 위에 적용시키면 모든 변수들에 적용이 가능하고, 변수 이름 위에 적용시키면 해당 변수들만 적용 가능하다. 아래의 예제에서는 모든 변수들에 대해 Getter 메소드를 만들고 상호병 변수에 대해서만 Setter 메소드를 만들어보도록 하겠다.

@AllArgsConstructor ]

@AllArgsConstructor는 모든 변수를 사용하는 생성자를 자동완성 시켜주는 어노테이션이다.

[ @NoArgsConstructor ]

@NoArgsConstructor는 어떠한 변수도 사용하지 않는 기본 생성자를 자동완성 시켜주는 어노테이션이다.

[ @RequiredArgsConstructor ]

@RequiredArgsConstructor는 특정 변수만을 활용하는 생성자를 자동완성 시켜주는 어노테이션이다. 생성자의 인자로 추가할 변수에 @NonNull 어노테이션을 붙여서 해당 변수를 생성자의 인자로 추가할 수 있다. 아니면 해당 변수를 final로 선언해도 의존성을 주입받을 수 있다.

[ @EqualsAndHashCode ]

@EqualsAndHashCode 어노테이션을 활용하면 클래스에 대한 equals 함수와 hashCode 함수를 자동으로 생성해준다.

[ @ToString ]

@ToString 어노테이션을 활용하면 클래스의 변수들을 기반으로 ToString 메소드를 자동으로 완성시켜 준다. 출력을 원하지 않는 변수에 @ToString.Exclude 어노테이션을 붙여주면 출력을 제외할 수 있다. 또한 상위 클래스에 대해도 toString을 적용시키고자 한다면 상위 클래스에 @ToString(callSuper = true) 를 적용시키면 된다. 

\

[ @Data ]

@Data 어노테이션을 활용하면 @ToString, @EqualsAndHashCode, @Getter, @Setter, @RequiredArgsConstructor를 자동완성 시켜준다. 실무에서는 너무 무겁고 객체의 안정성을 지키기 때문에 @Data의 활용을 지양한다.

[ @Builder ]

@Builder 어노테이션을 활용하면 해당 클래스의 객체의 생성에 Builder패턴을 적용시켜준다. 모든 변수들에 대해 build하기를 원한다면 클래스 위에 @Builder를 붙이면 되지만, 특정 변수만을 build하기 원한다면 생성자를 작성하고 그 위에 @Builder 어노테이션을 붙여주면 된다.

[ @Delegate ]

@Delegate 어노테이션은 한 객체의 메소드를 다른 객체로 위임시켜 준다.

[ @Log 관련 어노테이션 ]

@Log4j2와 같은 어노테이션을 활용하면 해당 클래스의 로그 클래스를 자동 완성 시켜준다. @Log 관련 어노테이션을 활용한 예제는 아래와 같다.

@RestController
@RequestMapping(value = "/store")
@Log4j2
public class StoreController {

    @GetMapping(value = "/log")
    private ResponseEntity log(){
        log.error("Error");
        return ResponseEntity.ok().build();
    }

}

출처: https://mangkyu.tistory.com/78 [MangKyu's Diary:티스토리]

 

Spring

 

  • Controller 는 제일 바깥 쪽에서 요청을 받고, 응답을 되돌려주는 역할을 합니다.
  • Service 는 중간에서 구체적인 작업 순서를 결정하고요.
  • Repository 는 DB와 직접 소통함으로써 자료를 생성하고, 조회하고, 변경하고, 삭제합니다.
  • 그리고 각 레이어 간에는 절대 Entity를 직접 사용하지 않고, DTO 라는 택배상자를 만들어 사용합니다.

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

항해 99 16일차  (0) 2022.10.04
항해 99 15일차  (0) 2022.10.03
항해 12일 차  (0) 2022.09.30
항해 11일 차  (0) 2022.09.29
항해 10일차  (0) 2022.09.28

인터페이스는 왜 필요한가?

다음은 어떤 동물원 사육사가 하는 일이다.

난 동물원의 사육사이다.
육식동물이 들어오면 난 먹이를 던져준다.
호랑이가 오면 사과를 던져준다.
사자가 오면 바나나를 던져준다.

이런 케이스를 코드로 담아보자. 다음과 같이 Animal, Tiger, Lion, Zookeeper 클래스를 작성하자.

Sample.java

class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Tiger extends Animal {
}

class Lion extends Animal {
}

class ZooKeeper {
    void feed(Tiger tiger) {  // 호랑이가 오면 사과를 던져 준다.
        System.out.println("feed apple");
    }

    void feed(Lion lion) {  // 사자가 오면 바나나를 던져준다.
        System.out.println("feed banana");
    }
}

public class Sample {
    public static void main(String[] args) {
        ZooKeeper zooKeeper = new ZooKeeper();
        Tiger tiger = new Tiger();
        Lion lion = new Lion();
        zooKeeper.feed(tiger);  // feed apple 출력
        zooKeeper.feed(lion);  // feed banana 출력
    }
}

이전 챕터에서 보았던 Dog 클래스와 마찬가지로 Animal을 상속한 Tiger와 Lion이 등장했다. 그리고 사육사 클래스인 ZooKeeper 클래스를 위처럼 정의 하였다. ZooKeeper 클래스는 호랑이가 왔을 때, 사자가 왔을 때 각각 다른 feed 메소드가 호출된다.

 

프로그램을 실행하면 다음과 같은 결과가 출력될 것이다.

feed apple
feed banana

자, 이제 다음을 생각 해 보자.

동물원에 호랑이와 사자뿐이라면 ZooKeeper 클래스는 더이상 할일이 없겠지만 악어, 표범등이 계속 추가된다면 ZooKeeper는 육식동물이 추가될 때마다 매번 다음과 같은 feed 메소드를 추가해야 한다.

아래 추가한 메소드는 실제로 코드에 적용하지 말고 눈으로만 보자.

(... 생략 ...)

class ZooKeeper {
    void feed(Tiger tiger) {
        System.out.println("feed apple");
    }

    void feed(Lion lion) {
        System.out.println("feed banana");
    }

    public void feed(Crocodile crocodile) {
        System.out.println("feed strawberry");
    }

    public void feed(Leopard leopard) {
        System.out.println("feed orange");
    }
}

(... 생략 ...)

이렇게 육식동물이 추가 될 때마다 feed 메소드를 추가해야 한다면 사육사(ZooKeeper)가 얼마나 귀찮겠는가? 이런 어려움을 극복하기 위해서는 인터페이스의 도움이 필요하다.

 

인터페이스 작성하기

다음과 같이 코드 상단에 육식동물(Predator) 인터페이스를 추가하자.

interface Predator {
}

class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

(... 생략 ...)

위 코드와 같이 인터페이스는 class가 아닌 interface 라는 키워드를 이용하여 작성한다.

※ 인터페이스는 클래스와 마찬가지로 Predator.java와 같은 단독 파일로 저장하는 것이 일반적인 방법이다. 여기서는 설명의 편의를 위해 Sample.java 파일의 최상단에 작성하였다.

그리고 Tiger, Lion 클래스는 작성한 인터페이스를 구현하도록(Implements) 수정하자.

(... 생략 ...)

class Tiger extends Animal implements Predator {
}

class Lion extends Animal implements Predator {    
}

(... 생략 ...)

인터페이스 구현은 위와같이 implements 라는 키워드를 사용한다.

이렇게 Tiger, Lion 클래스가 Predator 인터페이스를 구현하게 되면 ZooKeeper 클래스의 feed 메소드를 다음과 같이 변경 할 수 있다.

변경전

(... 생략 ...)

class ZooKeeper {
    void feed(Tiger tiger) {
        System.out.println("feed apple");
    }

    void feed(Lion lion) {
        System.out.println("feed banana");
    }
}

(... 생략 ...)

변경후

(... 생략 ...)

class ZooKeeper {
    void feed(Predator predator) {
        System.out.println("feed apple");
    }
}

(... 생략 ...)

feed 메소드의 입력으로 Tiger, Lion을 각각 필요로 했지만 이제 이것을 Predator라는 인터페이스로 대체할 수 있게 되었다. tiger, lion은 각각 Tiger, Lion의 객체이기도 하지만 Predator 인터페이스의 객체이기도 하기 때문에 위와같이 Predator를 자료형의 타입으로 사용할 수 있는 것이다. 상속에서 공부했던 IS-A 관계가 인터페이스에도 마찬가지로 적용된다. "Tiger is a Predator", "Lion is a Predator"가 성립된다.

  • tiger - Tiger 클래스의 객체, Predator 인터페이스의 객체
  • lion - Lion 클래스의 객체, Predator 인터페이스의 객체

※ 이와같이 객체가 한 개 이상의 자료형 타입을 갖게되는 특성을 다형성(폴리모피즘)이라고 하는데 이것에 대해서는 "다형성" 챕터에서 자세히 다루도록 한다.

이제 어떤 육식동물이 추가되더라도 ZooKeeper는 feed 메소드를 추가할 필요가 없다. 다만 육식동물이 추가 될 때마다 다음과 같이 Predator 인터페이스를 구현한 클래스를 작성하기만 하면 되는 것이다.

class Crocodile extends Animal implements Predator {
}

※ Crocodile 클래스는 실제 코드에 적용하지 말고 눈으로만 보자.

눈치가 빠르다면 이제 왜 인터페이스가 필요한지 감을 잡았을 것이다. 보통 중요 클래스를 작성하는 입장이라면 (여기서는 ZooKeeper가 중요한 클래스이다) 클래스의 구현체와 상관없이 인터페이스를 기준으로 중요 클래스를 작성해야만 한다. 구현체(Tiger, Lion, Crocodile,...)는 늘어날수 있지만 인터페이스(Predator)는 하나이기 때문이다.

인터페이스의 메소드

자, 그런데 위 ZooKeeper 클래스에 약간의 문제가 발생했다. 아래의 ZooKeeper클래스의 feed 메소드를 보면 호랑이가 오던지, 사자가 오던지 무조건 "feed apple" 이라는 문자열을 출력한다. 사자가 오면 "feed banana" 를 출력해야 하지 않겠는가!

(... 생략 ...)

class ZooKeeper {
    public void feed(Predator predator) {
        System.out.println("feed apple");  // 항상 feed apple 만을 출력한다.
    }
}

(... 생략 ...)

이번에도 인터페이스의 마법을 부려보자.

Predator 인터페이스에 다음과 같은 메소드를 추가 해 보자.

interface Predator {
    String getFood();
}

(... 생략 ...)

getFood 라는 메소드를 추가했다. 그런데 좀 이상하다. 메소드에 몸통이 없다?

인터페이스의 메소드는 메소드의 이름과 입출력에 대한 정의만 있고 그 내용은 없다. 그 이유는 인터페이스는 규칙이기 때문이다. 위에서 설정한 getFood라는 메소드는 인터페이스를 implements한 클래스들이 구현해야만 하는 것이다.

인터페이스에 위처럼 메소드를 추가하면 Tiger, Lion 등의 Predator 인터페이스를 구현한 클래스들에서 컴파일 오류가 발생할 것이다. 오류를 해결하려면 다음처럼 Tiger, Lion 클래스에 getFood 메소드를 구현해야 한다.

 

(... 생략 ...)

class Tiger extends Animal implements Predator {
    public String getFood() {
        return "apple";
    }
}

class Lion extends Animal implements Predator {
    public String getFood() {
        return "banana";
    }
}

(... 생략 ...)

※ 인터페이스의 메소드는 항상 public으로 구현해야 한다.

Tiger, Lion 클래스의 getFood 메소드에 육식동물의 먹이인 "apple", "banana"를 각각 리턴하도록 작성했다. 이렇게 getFood 메소드를 추가하면 컴파일 오류가 해결될 것이다.

이제 ZooKeeper 클래스도 다음과 같이 변경이 가능하다.

(... 생략 ...)

class ZooKeeper {
    void feed(Predator predator) {
        System.out.println("feed "+predator.getFood());
    }
}

(... 생략 ...)

feed 메소드가 "feed apple" 을 출력하던 것에서 "feed "+predator.getFood()를 출력하도록 변경되었다. predator.getFood()를 호출하면 Predator 인터페이스를 구현한 구현체(Tiger, Lion)의 getFood() 메소드가 호출된다.

그리고 프로그램을 실행해 보자. 원하던 데로 다음과 같은 결과값이 출력되는 것을 확인할 수 있을 것이다.

feed apple
feed banana

05-07 인터페이스 - 점프 투 자바 (wikidocs.net)

'JAVA > 참고 개인공부' 카테고리의 다른 글

JVM 개념 및 구조  (0) 2022.12.09
핵클 협력사 세션.  (0) 2022.10.04

주특기 Spring 본격 시작

JVM, JRE, JDK의 차이

자바에서 사용하는 용어 중 혼동하기 쉬운 JVM, JRE, JDK에 대해서 정리해 보자.

JVM

JVM은 자바 가상머신(Java Virtual Machine)의 약자이다.

JVM은 자바 소스코드로부터 만들어지는 자바 바이너리 파일(.class)을 실행할 수 있다. 또한 JVM은 플랫폼에 의존적이다. 즉 리눅스의 JVM과 윈도우즈의 JVM은 다르다. 단, 컴파일된 바이너리 코드는 어떤 JVM에서도 동작시킬 수 있다.

JVM은 다음과 같은 역할을 한다.

  • 바이너리 코드를 읽는다.
  • 바이너리 코드를 검증한다.
  • 바이너리 코드를 실행한다.
  • 실행환경(Runtime Environment)의 규격을 제공한다. (필요한 라이브러리 및 기타파일)

JRE

JRE는 자바 실행환경(Java Runtime Environment)의 약자이다.

JRE는 JVM 이 자바 프로그램을 동작시킬 때 필요한 라이브러리 파일들과 기타 파일들을 가지고 있다. JRE는 JVM의 실행환경을 구현했다고 할 수 있다.

JDK

JDK는 자바 개발도구(Java Development Kit)의 약자이다.

JDK는 JRE + 개발을 위해 필요한 도구(javac, java등)들을 포함한다.

이상과 같이 jvm, jre, jdk 에 대해서 알아보았다.

A2 JVM, JRE, JDK의 차이 - 점프 투 자바 (wikidocs.net)

 

메모리

 

 

출처 : http://huelet.tistory.com/entry/JVM-%EB%A9%94%EB%AA%A8%EB%A6%AC%EA%B5%AC%EC%A1%B0

 

메서드 영역

 

메소드 영역은 Class Area, Code Area, Static Area로 불려지며, 의미상 공유 메모리 영역이라고도 불린다. 코드에서 사용되는 클래스들을 클래스 로더로 읽어 클래스 별로 런타임 상수 풀(runtime constant pool), 필드(field) 데이터, 메소드(method) 데이터, 메소드 코드, 생성자 코드 등을 분류해서 저장한다. 메소드 영역은 JVM이 동작해서 클래스가 로딩될때 생성되고, 모든 스레드가 공유하는 영역이다.

 

1. 클래스 내부에서는 직접 접근이 가능하다.(왜? 라고 생각 되면, 위의 내용을 다시 한번...)

public class StaticExample { 
  static int count = 0;

  StaticExample() {}

  public void increase() {
    count += 1;
  }
}

 

 

2. 클래스 이름으로 접근, 인스턴스 생성 후 접근(인스턴스 변수와의 구분이 어렵다. 권장하지 않는다.)

 

public class Main { 
  public static void main(String[] args) {
    StaticExample.count++; //클래스 이름으로 접근

    StaticExample staticExample = new StaticExample();
    staticExample.count++;  //인스턴스 생성 후 접근(권장하지 않음.)
}

 

스텍 영역

 

Stack

  • Heap 영역에 생성된 Object 타입의 데이터의 참조값이 할당된다.
  • 원시타입의 데이터가 값과 함께 할당된다.
  • 지역변수들은 scope 에 따른 visibility 를 가진다.
  • 각 Thread 는 자신만의 stack 을 가진다.

Stack 에는 heap 영역에 생성된 Object 타입의 데이터들에 대한 참조를 위한 값들이 할당된다. 또한, 원시타입(primitive types) - byte, short, int, long, double, float, boolean, char 타입의 데이터들이 할당된다. 이때 원시타입의 데이터들에 대해서는 참조값을 저장하는 것이 아니라 실제 값을 stack 에 직접 저장하게 된다.

 

Heap area(힙 메모리 영역)

 

 인스턴스를 생성하는 방법은 "클래스 변수 =  new 클래스();" 라는 것을 기억하면서 밑에 내용을 확인해 보자.
 참조형(Reference Type)의 데이터 타입을 갖는 객체(인스턴스), 배열 등은 Heap 영역에 데이터가 저장된다. 이때 변수(객체, 객체변수, 참조변수)는 Stack 영역의 공간에서 실제 데이터가 저장된 Heap 영역의 참조값(reference value, 해시코드 / 메모리에 저장된 주소를 연결해주는 값) new 연산자를 통해 리턴 받는다. 다시 말하면 실제 데이터를 갖고 있는 Heap 영역의 참조 값을 Stack 영역의 객체가 갖고 있다이렇게 리턴 받은 참조 값을 갖고 있는 객체를 통해서만 해당 인스턴스를 핸들 할 수 있다.

 
public class HeapAreaEx01 {
    public static void main(String[] args) {
        int[] a = null; // int형 배열 선언 및 Stack 영역 공간 할당
        System.out.println(a); // 결과 : null
        a = new int[5]; // Heap 영역에 5개의 연속된 공간 할당 및 
        // 변수 a에 참조값 할당
        System.out.println(a); // 결과 : @15db9742 (참조값)
    }
}

 설명은 소스 코드의 주석 처리된 부분과 같다. 결국 a변수는 데이터가 저장된 Heap영역의 참조 값을 리턴 받아 갖고 있다는 것이다. 다른 예제도 확인해보자.

 

 
public class HeapAreaEx02 {
    public static void main(String[] args) {
        String str1 = new String("joker");
        String str2 = new String("joker");
        if(str1 == str2){
            System.out.println("같은 주소값 입니다.");
        }else{
            System.out.println("다른 주소값 입니다.");
        }
    }
}

 

 문자열을 저장하는 String도 참조형이다. new 연산자를 이용해서 생성하면 데이터는 Heap 영역에 저장되고 str1과 str2는 참조 값을 리턴 받는다. 저장된 주소가 다르기 때문에 "=="으로 비교 시 "다른 주소값 입니다."가 출력되는 것이다. 마지막으로 예제를 하나 더 살펴보자.

 

 

class A{}

public class HeapArea {
    public static void main(String[] args) {
        A a = null; // A타입의 a객체 선언 및 Stack 영역 공간 할당
        System.out.println(a); // 결과 : null
        a = new A(); // Heap 메모리에 공간 할당 및 객체(a)에 참조값 할당
        System.out.println(a); // 결과 : @15db9742
    }
}

 소스 코드에 주석 처리한 내용으로 설명은 대신하겠다. 결국 객체가 참조 값을 갖는다는 것이다.

 참고로 Heap에 저장된 데이터가 더 이상 사용이 불필요하다면 메모리 관리를 위해 JVM(자바 가상머신)에 의해 알아서 해제된다. 이러한 기능을 가비지컬렉션(GC, 쓰레기 수집)이라고 한다. 
 메모리 관련 내용은 여기서 마무리하겠다. 메모리는 중요한 부분이고 인스턴스 등을 정의할 때 왜 이렇게 사용하는지 이해하는 것이 코딩하는데 있어 많은 도움이 될 것이다. 

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

항해 99 15일차  (0) 2022.10.03
항해 99 13일 차  (0) 2022.10.01
항해 11일 차  (0) 2022.09.29
항해 10일차  (0) 2022.09.28
항해 99 9일차  (0) 2022.09.27

오늘은 알고리즘 테스트가 있는 날!!

잘 할 수 있을 지 두려워 아침부터 알고리즘에 대해 더 공부하면서 여러가지 찾아보았다.

프로그래밍 문제 해결 과정, 전략

  ㅇ 문제 해결 과정

 

    1. 문제를 읽고 이해하기

      - 문제 설명을 공격적으로 읽으며 문제가 원하는 바를 완전히 이해하는 과정이 반드시 필요

 

    2. 문제를 익숙한 용어로 재정의하기 + 추상화

      - 자신이 다루기 쉬운 개념을 이용하여 문제를 자신의 언어로 풀어 쓰는 단계

      - 본질만 남겨두고 축약하여 다루기 쉽게 표현 -> 추상화

      - 어떤 부분을 추상화할 것인지를 선택하는 작업과 문제를 재정의하는 방법들에 대한 고찰은 좋은 프로그래머가 되기 위해 필수적인 과정

 

    3. 어떻게 해결할지 계획 세우기

      - 문제를 어떤 방식으로 해결할지 결정하고, 사용할 알고리즘과 자료구조를 선택

      - 문제 해결에서 가장 중요한 단계

 

    4. 계획 검증하기

      - 설계한 알고리즘이 모든 경우에 요구 조건을 정확히 수행하는지 증명

      - 수행에 걸리는 시간과 사용하는 메모리가 문제의 제한 내에 들어가는지 확인

 

    5. 계획 수행하기(프로그램으로 구현하기)

      - 프로그램을 작성하는 단계

 

    6. 어떻게 풀었는지 돌아보고, 개선할 방법이 있는지 찾아보기(회고하기)

      - 당장 직접적인 영향은 없지만 장기적으로 가장 큰 영향을 미치는 단계

      - 1. 문제를 풀 때마다 코드와 함께 자신의 경험을 기록으로 남기기(간단한 해법, 접근 방식, 결정적이었던 깨달음 등)

      - 한 번에 맞추지 못한 경우 오답 원인도 기록

      - 2. 같은 문제를 해결한 다른 사람의 코드를 보기

      - 다른 사람의 의견이나 답안을 통해 자극을 얻다보면 더 효율적으로 공부할 수 있음(그룹 스터디, 인터넷)

 

    +. 문제를 풀지 못했을 경우

      - 한 문제에 너무 매달려 있는 것도 좋지 않음

      - 일정 시간이 지나도록 고민해도 답을 찾지 못할 때는 다름 사람의 소스 코드나 풀이를 참조하는 것도 좋은 자세

      - 자신이 왜 이 풀이를 떠올리지 못했는지 돌아보기

 

 

#. 문제 해결 전략

 

 ㅇ 문제 해결 전략

    - 직관과 체계적인 접근 : 문제와 답의 구조에 대한 직관의 중요성

    - 체계적인 접근을 위한 질문 : 문제를 해결할 때 유용한 질문

 

    1. 비슷한 문제를 풀어본 적이 있었는지?

      - 문제의 목적을 보고 적절한 접근 방법을 선택하기 위해서는 어떤 문제가 최적화 문제인지, 경우의 수를 구하는 문제인지, 검색 문제인지 등을 분류하는 방법을 익히고, 각 알고리즘들이 어느 경우에 사용될 수 있는지 체계적으로 공부

 

    2. 단순한 방법에서 시작할 수 있을지?

      - 좀 더 효율적인 자료 구조를 사용하거나 계산 과정에서 같은 정보를 두 번 중복으로 계산하지 않는 등의 최적화

      - ex) 3명의 아이에게 20개 사탕을 배분할 때 사탕 총 량의 순서는 상관 없음

 

    3. 내가 문제를 푸는 과정을 수식화 할 수 있을지?

 

    4. 문제를 단순화할 수 없을지?

      - 주어진 문제의 좀더 쉬운 변형판을 먼저 풀어보기

      - ex) 문제의 제약 조건 없애기, 계산해야 하는 변수의 수 줄이기, 다차원의 문제를 1차원으로 줄여 표현해보기 등)

 

    5. 그림으로 그려볼 수 있을지?

      - 문제에 관련된 그림을 그려 보는 것

 

    6. 수식으로 표현할 수 있는지?

      - 평문으로 쓰여 있는 문제를 수식으로 표현하는 것도 도움이 되는 경우가 있음

 

    7. 문제를 분해할 수 있을지?

      - 더 다루기 쉬운 형태로 문제를 변형

 

    8. 뒤에서부터 생각해서 문제를 풀 수 있을지?

      - 문제에 내재된 순서를 바꿔 보는 것

      - ex) 사다리 게임에서 A에서 B로 가는 방법을 찿기란 어렵지만 B에서 A로 가는 방법을 찾기는 쉬움

 

    9. 순서를 강제할 수 있을지?

      - 순서가 없는 문제에 순서를 강제해서 문제를 풀어보기

      - ex) 2차원 격자를 이용한 문제, 경우의 수

 

    10. 특정 형태의 답만을 고려할 수 있을지?

출처: https://data-make.tistory.com/220 [Data Makes Our Future:티스토리]

 

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

항해 99 13일 차  (0) 2022.10.01
항해 12일 차  (0) 2022.09.30
항해 10일차  (0) 2022.09.28
항해 99 9일차  (0) 2022.09.27
항해 99 8일차  (0) 2022.09.26

+ Recent posts