API Key

  • 서비스 확대로 기능들이 분리되면서, Module이나 Application들간의 공유와 독립성을 보장하기 위한 기능들이 생겨남
  • 동작 방식
    • 1. 사용자는 API Key를 발급받는다.
    • 2. 해당 API를 사용하기 위해 Key와 함께 요청을 보낸다.
    • 3. Application은 요청이 오면 Key를 통해 User정보를 확인해서 누구의 key인지 확인
    • 4. 해당 key의 인증과 인가에 따라 데이터를 사용자에게 반환
  • 문제점 : key가 유출되면 대비하기 힘들다. -> key 업데이트의 번거로움

 

 

OAuth(Open Authorization)

  • 인터넷 사용자들이 비밀번호를 제공하지 않고 
  • 다른 웹 사이트 상의 자신들의 정보에 대해
  • 웹 사이트나 애플리케이션의 접근 권한을 부여할 수 있는 개방형 표준
  • 간편 로그인
  • API Key 단점 보완
  • 사용 용어
    • 사용자 : 계정을 가지고 있는 개인
    • 소비자 : OAuth를 사용해 서비스 제공자에게 접근하는 웹사이트 or 애플리케이션
    • 서비스 제공자 : OAuth를 통해 접근을 지원하는 웹 애플리케이션
    • 소비자 비밀번호 : 서비스 제공자에서 소비자가 자신임을 인증하기 위한 키
    • 요청 토큰 : 소비자가 사용자에게 접근 권한을 인증받기 위해 필요한 정보
    • 접근 토큰 : 인증 후에 사용자가 서비스 제공자가 아닌 소비자를 통해 보호 자원에 접근하기 위한 키 값
    • 토큰의 종류
      • Access Token : 만료시간 존재. 다시 요청 가능
      • Refresh Token : 만료되면 다시 처음부터 진행
  • 문제점
    • Token이 규칙 없이 발행되기 때문에 증명확인이 필요하다.

인증 과정

1. 사용자가 App의 기능을 사용하기 위한 요청을 보낸다.

2. App은 해당 사용자가 로그인이 되어있는지 확인

3. App은 사용자가 로그인이 되어있지 않으면 사용자를 인증서버로 Redirection

4. 간접적으로 Autorize 요청을 받은 인증 서버는 해당 사용자가 회원인지 인증서버에 로그인 되어있는지 확인

5. 사용자가 최초의 요청에 대한 권한이 있는지 확인(GRANT)

6. 사용자가 Grant 요청을 받게 되면 사용자는 해당 인증정보에 대한 허가를 준다.

7. 인증서버에서 인증과 인가에 대한 과정이 모두 완료되면 App에게 인가코드

8. 인가코드의 유효시간은 짧기 때문에, App은 해당 코드를 Request Token으로 사용하여 인증 서버에 요청을 보낸다.

9. 인증서버는 자신의 저장소에 저장한 코드와 일치한지 확인하고 실제 리소스에 접근하게 될 Access Token 발급

10. 이제 App은 Access Token을 사용해서 업무 처리 가능

 

 

JWT(JSON Web Token)

  • 토큰 작성에 대한 규약
  • 웹 표준으로, 두 개체에서 JSON 객체를 사용하여 가볍고 자가수용적인 방식으로 정보를 안정성 있게 전달
  • 인증 여부 확인을 위한 값, 유효성 검증을 위한 값 그리고 인증 정보 자체를 담고 있기 때문에 인증 서버에 묻지 않아도 사용 가능
  • 문제점
    • 토큰 자체가 인증 정보를 가지고 있기 때문에 민감한 정보는 인증 서버에 다시 접속하는 과정이 필요하다.

 

JWT 구성요소

 

  • 3가지 문자열로 구성
  • 1. header(헤더) : JWT 서명에 사용된 알고리즘을 담는다. 
    • typ : 토큰의 타입
    • alg : 해싱 알고리즘 
  • 2. payload(정보) : 토큰에 담긴 주체들을 담는다. 
    • 토큰을 담을 정보
    • iss : 토큰 발급자 (issuer)
    • sub : 토큰 제목 (subject)
    • aud : 토큰 대상자 (audience)
    • exp : 토큰 만료 시간(expiration)
    • nbf : 토큰의 활성 날짜(Not before)
    • iat : 토큰이 발급된 시간(issued at)
    • jti : JWT 고유 식별자
  • 3. signature(서명) : 헤더와 페이로드를 각각 base64로 인코딩하고 콤마로 이어 붙인다. 그리고 헤더에 있는 알고리즘으로 암호화된 값을 담는다.
    • 클라이언트에서 받은 토큰의 데이터 조작 여부를 확인하는 곳 
    • 헤더의 인코딩 값과 정보의 인코딩 값을 합친 후 주어진 비밀키로 해쉬하여 생성한다. (header+payload+salt = hash)
    • 이렇게 만든 해쉬는 base64 형태로 나타낸다.
    • base64 : 8비트 이진 데이터를 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자들로만 이루어진 일련의 문자열로 바꾸는 인코딩 방식. 통신과정에서 binary data 손실을 막기 위해 사용한다.

 

로그인 인증과 JWT

  • 유효기간이 짧은 토큰은 사용자 입장에서 자주 로그인을 해야해서 번거롭다.
  • 유효기간이 긴 토큰은 편하지만 토큰을 탈취당할 약점이 있다.
  • Refresh Token : 위의 단점을 보완하기 위해 사용
    • Access Token의 유효기간이 만료되었을 때, Refresh Token이 새로 발급
    • Refresh Token이 만료되면, 새로 로그인
  • 토큰 저장 장소
    • Access Token : 클라이언트(cookie)
    • Refresh Token : 서버(DB)

 

 

 

 

JWT와 OAuth 2.0의 차이

- 둘 다 인증 방식이라는 공통점이 있지만, 서로 목적이 다르다.

- OAuth 2.0 : 하나의 플랫폼의 권한(아무 의미없는 무작위 문자열 토큰)

다양한 플랫폼에서 권한을 행사할 수 있게 해줌으로써 리소스 접근이 가능하게 하는데 목적을 두고있다.

 

- JWT는 Cookie, Session을 대신하여 의미있는 문자열 토큰

로그인 세션이나 주고받는 값이 유효한지 검증할 때 주로 사용

자바 스크립트와 Node.js

- 비동기 프로그래밍으로 이벤트 주도 방식을 사용하기 때문에 콜백 함수 사용

- 콜백 함수 : 매개변수를 통해서 다른 함수의 내부로 전달되는 함수

- ES2015부터 콜백 대신 API들이 프로미스 기반으로 재구성

 

프로미스(Promise)

- 자바스크립트 비동기 처리에 사용되는 객체

- ECMAScript6- 비동기 처리 : 특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 자바스크립트의 특성- 프로미스를 사용하면, 비동기 작업들을 순차적으로 진행하거나, 병렬로 진행하는 컨트롤이 수월해진다.

- new Promise로 프로미스 생성

- resolve, reject를 매개변수로 갖는 콜백 함수를 넣는 방식 

- resolve가 호출되면 then, reject가 호출되면 catch가 실행 된다.

- resolve 호출 -> resolve('성공') -> message에 '성공'이 들어가 출력

- reject 호출 -> resolve('실패') -> message에 '실패'가 들어가 출력

const condition = true;
 
const promise = new Promise((resolve, reject) => {
    if (condition){
        resolve('성공');
    } else {
        reject('실패');
    }
});
 
promise
    .then((message) => {
        console.log(message);
    })
    .catch((error) => {
        console.log(error);
    });

 

Promise.all

- 프로미스를 여러개 한꺼번에 실행

- 모든 프로미스가 resolve 상태여야 then 실행

const promise1 = Promise.resolve('성공1');
const promise2 = Promise.resolve('성공2');
 
Promise.all([promise1, promise2])
    .then((result) => {
        console.log(result);
    })
    .catch((error) => {
        console.error(err);
    });

 

async/await

  • ES2017에 추가된 기능
  • Node version 7.6
  • 비동기 프로그래밍을 할 때 유용하게 사용되고, 콜백의 복잡성을 해결하기 위한 프로미스를 조금 더 깔끔하게 만들어주는 역할
  • 기존 프로미스 사용 코드 
function findAndSaveUser1(Users) {
    Users.findOne({})
        .then((user) => {
            user.name = 'kim';
            return user.save();
        })
        .then((user) => {
            return Users.findOne({gender: 'm'});
        })
        .then((user) => {
            // 생략
        })
        .catch(err => {
            console.error(err);
        });
}
  • async/await 문법 사용
    • function 앞에 async를 붙여주고
    • 프로미스 앞에 await를 붙여준다.
async function findAndSaveUser(Users) {
    try{
        let user = await Users.findOne({});
        user.name = 'kim';
        user = await user.save();
        user = await Users.findOne({gender: 'm'});
        // 생략
 
    } catch(err) {
        console.error(err);
    } 
}

자바스크립트 작동 원리

  • 자바스크립트?
    • 싱글 스레드 기반으로 동작하는 자바스크립트
    • 이벤트 루프를 기반으로 동작하는 싱글 스레드 Node.js

 

JS Engine 종류

- 클라이언트에서 JavaScript를 실행하려면 JavaScript 엔진이 필요하다 -> 브라우저에 내장되어 있다.

- Chrome : V8

- Edge : Chakra

- Safari : Webkit

- Firefox : SpiderMonkey

 

JS Engine vs Rendering Engine

  • Rendering Engine : HTML, CSS으로 작성된 마크업 관련된 코드를 웹 페이지에 rendering하는 역할
  • Javascript Engine : Javascript으로 작성한 코드를 해석하고 실행하는 인터프리터

 

이벤트 핸들러

  • 이벤트 : 브라우저에서 사용자의 조작이나 환경의 변화로 벌어진 사건
  • 핸들러 : 이벤트에 대한 즉각적인 반응을 할 수 있도록 해주는 것

 

 

JS Engine의 3가지 영역

  • Call Stack
    • 하나의 call stack 사용 -> 하나의 함수가 실행되면 이 함수의 실행이 끝나기 전까지 다른 task 수행 불가
  • Task Queue(event Queue)
    • JS의 런타임 환경에서 처리해야하는 Task들을 임시 저장하는 대기큐
    • call stack이 비어졌을 때 먼저 대기열에 들어온 순서대로 수행
    • 자바스크립트에서 이벤트에 의해 실행되는 함수(핸들러)들은 비동기로 실행된다.
    • 비동기로 호출되는 함수는 call stack이 아닌 task queue에 enqueue
  • heap
    • 동적으로 생성된 객체(인스턴스)는 힙에 저장된다.
  • Event Loop
    • event loop와 queue를 이용해서 동시에 여러 작업이 처리되는 것처럼 비동기 작업을 수행한다.
    • 반복해서 call stack과 queue 사이의 작업들을 확인해서, call stack이 비워있는 경우 queue의 작업을 꺼내고 call stack에 넣는다.
function three() {
  console.log('3')
}

function two() {
  console.log('2')
}

function one() {
  console.log('1') // 1 출력
}

one() // one 실행

setTimeout(() => { // 익명함수는 event queue
  two()
}, 1000)

three() // three 실행

 

JS 엔진이 코드를 실행하기 위해 기동되면

1. 빌트인 객체(생성자 함수 포함) 생성

2. 실행 환경에 맞는 global 객체 생성

  • browser 환경 : window 객체
  • node.js 환경 : global 객체
  • 자바스크립트 엔진이 소스코드를 불러와서 기동되면, 기본적인 빌트인 객체들을 만들어서 글로벌 객체(전역 객체)를 만들게 된다.
  • 실행되는 환경에 따라 달라진다.

 


this 

  • 자바스크립트의 모든 함수는 실행될 때 마다 함수 내부에 this 객체가 추가된다.
  • arguments(유사 배열 객체)와 함께 함수 내부로 암묵적 전달
  • 함수가 호출된 상황에 따라 this가 달라진다.

1. 객체의 메소드 호출을 할 경우

  • 메소드 : 객체의 프로퍼티가 함수인 경우
  • 메소드를 호출할 경우 메소드를 호출한 객체를 바인딩 -> 현재 사용하는 객체

2. 함수를 호출할 경우

  • 함수 내부 코드에 사용된 this는 전역 객체에 바인딩 -> window

3. 생성자 함수를 통해 객체를 생성할 때

  • new 연산자 사용
  • this는 객체 자신 
    • 빈 객체 생성 -> this 바인딩 -> 함수를 통해 생성된 객체, 부모 프로토타입 객체와 연결되어 있다.
  • return문이 명시되어 있지 않은 경우 this는 바인딩 된 새로 생성한 객체가 리턴된다.
var myObject = {
  name: "foo",
  sayName: function() {
    console.log(this);
  }
};
myObject.sayName();
// console> Object {name: "foo", sayName: sayName()}
var value = 100;
var myObj = {
  value: 1,
  func1: function() {
    console.log(`func1's this.value: ${this.value}`);

    var func2 = function() {
      console.log(`func2's this.value ${this.value}`);
    };
    func2();
  }
};

myObj.func1();
// console> func1's this.value: 1
// console> func2's this.value: 100
var Person = function(name) {
  console.log(this);
  this.name = name;
};

var foo = new Person("foo"); // Person
console.log(foo.name); // foo

 

 

동기(Synchronous) VS 비동기(Asynchronous)

  • 동기 : 함수A가 함수B의 작업 완료 후 리턴을 기다리거나, 작업의 완료(리턴)를 계속해서 확인하는 작업 흐름
  • 비동기 : 함수 A가 함수 B를 호출할 때 콜백 함수를 함께 전달하여 함수 B의 작업이 완료되면 함께 보낸 함수를 실행
    • 이전에 호출한 함수의 작업 완료 여부를 신경쓰지 않는 상황
    • call back이 오기 전까지 신경쓰지 않고 다른 일을 할 수 있다.
  • 호출되는 함수의 작업 완료 여부를 신경쓰냐, 함수 실행/리턴 순차적인 흐름을 따르는가

 

 

블로킹(blocking) vs 논블로킹(non-blocking)

  • 블로킹
    • A 함수가 B를 호출하면 제어권을 B 함수에게 넘겨짐
    • A에겐 제어권이 없기 떄문에 실행을 멈춤
    • B함수 실행이 끝나면 제어권을 다시 A가 받아서 실행
  • 논블로킹
    • A함수가 B함수를 호출해도 본인 행동의 제어권을 본인(A)이 가지고 있음
    • B 함수가 실행되지만 A는 B를 호출한 이후에도 자신의 코드를 계속 실행할 수 있다.

 

 

4가지의 조합적 동작의 차이

  • Sync - Blocking
    • 함수 A는 함수 B의 리턴값을 필요로한다. (동기)
    • 제어권을 함수 B 에게 제어권을 넘겨준다. (블로킹)
    • 함수 B가 실행을 완료하여 리턴값과 제어권을 돌려줄 때까지 기다린다. 
  • Sync - NonBlocking
    • A 함수는 B 함수를 호출한다.
    • 이 때 A 함수는 B 함수에게 제어권을 주지 않고 본인의 코드를 계속 실행한다 (논블로킹)
    • 하지만 A 함수는 B 함수의 리턴값이 필요하기 때문에, 계속 B 함수에게 함수 실행을 완료했는지 물어본다 (동기)
      • 대기 시간동안 다른 일 수행 가능
  • Async - Nonblocking
    • A 함수는 B 함수를 호출한다.
    • 이 때 제어권을 A는 계속 가지고 있어서 B 함수를 호출한 이후에도 멈추지 않고 자신의 코드를 계속 실행 (논블로킹)
    • B 함수를 호출할 때 콜백 함수를 같이 넘겨서 B 함수가 작업이 끝나면 콜백함수 실행을 통해 리턴 전달 (비동기)
  • Async - Blocking
    • A 함수는 B 함수를 호출
    • 제어권을 함수 B 에게 제어권을 넘겨준다. 제어권을 받기 전까지 기다린다. (블로킹)
    • B 함수를 호출할 때 콜백함수를 같이 넘겨서 B 함수가 작업이 끝나면 콜백함수 실행을 통해 리턴 전달 (비동기)
     

'CS > 네트워크' 카테고리의 다른 글

[네트워크] HTTP와 HTTPS, TCP & UDP, GET & POST, OSI 7 계층  (0) 2022.09.30

Spring WeFlux

  • Spring 5에 추가된 모듈
  • 클라이언트, 서버에서 reactive 스타일의 어플리케이션 개발을 도와주는 모듈
  • async + non-block + reactive stream 지원
    • reactive Stream : Non-blocking backpressure를 통한 비동기 스트림 처리 표준을 제공하기 위한 명세
  • 장점 : 고성능, netty 지원, 비동기 non-blocking 메시지 처리

 

Back Pressure

  • 옵저버 패턴 : 발행자(publisher)가 구독자(subscriber)에게 데이터를 밀어넣는 방식
  • 발행자의 데이터 전송이 더 빠를 경우, 큐를 사용해서 대기중인 이벤트를 저장해야 한다. -> 오버플로우(overflow)
  • 고정 길이 버퍼 : 신규로 수신된 메시지 거절 -> 거절된 메시지를 재요청 하는 CPU 연산 비용 낭비
  • 가변 길이 버퍼 : out of memory 에러로 서버 크래시(crash) 
  • 백 프레셔 기본 원리 : 풀 방식 사용
    • 구독자가 처리할 수 있는 만큼의 데이터를 발행자에게 요청한다.
    • Dynamic pool 방식의 데이터를 요청해서 구독자가 수용할 수 있는 만큼의 데이터를 요청하는 방식

 

Netty

  • 프로토콜 서버 및 클라이언트와 같은 네트워크 응용 프로그램을 빠르고 쉽게 개발할 수 있는 NIO(Non-Blocking Input Output) 클라이언트 서버 프레임워크

 

Spring WebFlux vs Spring MVC

  • MVC : Servlet stack 기반으로 요청 한 개에 대해 하나의 스레드가 처리하고 Sync + blocking
  • WebFlux : Reative stack 기반으로 다수의 요청에 대해 1개의 스레드 처리 Async + non blocking

 

Spring WebFlux 사용이유

  • 하나의 요청에 대해 여러 외부 시스템에 대한 요청이 필요한 시스템에서 가장 빠른 응답을 주기 위해
  • 수 많은 요청을 처리하는 상황에서 스레드 지옥에서 벗어나기 위해
  • MVC는 1:1 요청처리 방식이기 때문에 트래픽이 몰리면 많은 스레드가 발생하고, 이 때 스레드가 전환될 때도 context switching 비용이 발생해 스레드가 많을 수록 비용이 커지게 된다.
  • WebFlux는 Event-Driven과 Async + Non-Blocking I/O를 통해 리소스를 효율적으로 사용한다.
  • MVC도 servlet 버전이 올라가면서 비동기 / 논블로킹이 가능하다.
    • MVC servlet 3.2 이후
    • 1. reqeust를 받은 서블렛 스레드는 pool에게 반납
    • 2. 특정 이벤트 발생 -> 서블릿 스레드 풀에 슬데ㅡ 요청
    • 3. 서블렛 스레드를 할당받고 작업 수행 후 사용자에게 response
    • 하지만 DB 접근, API call 등 모든 부분이 non blocking으로 구현하기에는 부족하다.

Class(클래스)

  • 객체를 정의하는 툴, 또는 설계도와 같은 의미로 사용된다.
  • 클래스는 멤버(member)로 속성을 표현하는 필드(field)와 기능을 표현하는 메소드(method)를 가진다.
  • 필드 : 객체의 상태
  • 메소드 : 객체의 행동
  • 인스턴스(Instance) 
    • 클래스를 사용하기 위해 해당 클래스 타입의 객체(Object)를 선언해야 한다.
    • 클래스의 인스턴스화 : 클래스로부터 객체를 선언하는 과정
    • 인스턴스는 선언된 해당 클래스 타입의 객체이다. -> 인스턴스는 메모리에 할당된 객체
  • 메소드(method)
    • 메소드 : 특정 작업을 수행하기 위한 명령문의 집합
    • 중복 코드 최소화 -> 가독성 향상
    • 유지보수 용이
  • 생성자(consructor)
    • 생성된 객체의 필드를 초기화해주는 메소드
    • 객체의 생성과 동시에 인스턴스 변수를 원하는 값으로 초기화할 수 있다.
    • 생성자도 메소드 이기 때문에, 메소드 오버로딩 가능 하지만, return X
    • 1. class는 무조건 생성자를 1개 이상 갖는다. : 없으면 default constructor 자동 생ㅅ어
    • 2. class 이름과 동일
    • 3. instance 초기화 담당
  • 필드(field)
    • 클래스에 포함된 변수(variable)
class { -> instance 생성 

1) fields

2) constructors

3) methods

}
public class Student{
	// fields (변수)
	String stuName; // String -> class -> reference Type -> 문자
	String stuNum; // 학번(00231123) : 연산이 필요하면 int, 아니면 사용하기 편한 String
	int stuAge; // 나이 - 숫자
	String stuDept; // 학과 - 문자열
	
	// 생성자 - 클래스 이름과 동일, 메소드와 유사(메소드는 Return이 있음)
	// 인스턴스를 초기화해주는 역할 
	// default constructor 
	Student() {
		
	}
	
	// methods (함수)
	public static void main(String args[]) {
		Student stu = new Student(); // instance 생성 : new + constructor
		// . operator ( . 연산자 )
		stu.stuName = "홍길동"; 
		stu.stuNum = "00231123";
		stu.stuAge = 20;
		stu.stuDept = "컴퓨터";
		
		System.out.println();
	}
}

 

 

상속(inheritance)

  • 부모 class가 가지는 내용을 확장해서 자식 class를 만드는 방식
  • 기존 클래스에 기능을 추가하거나 재정의해서 새로운 클래스를 정의하는 것
  • 장점
    • 기존 클래스를 재활용 가능
    • 중복 멤버를 미리 부모 클래스에 작성하면 자식 클래스에서 작성하지 않아도 된다.
    • 클래스 간의 계층 관계를 구성함으로써 다형성의 문법적 토대를 마련한다.
  • 자바의 다중 상속이 없는 이유?
    • 두 곳에서 동시에 상속을 받으면 같은 변수가 올 수 있는 문제점을 배제
class Human {
    // String name
}
class Person {
    String name; // 이름
    String mobile; // 전화번호
}

// tightly coupled
class Student extends Person{
    String dept; // 학과
}

class Teacher extends Person{
    String subject; // 과목
}

class Staff extends Person{
    int salary; // 월급
}

 

Object 클래스

  • 모든 클래스의 부모 클래스
  • 기존 Object에 있는 메소드들을 오버라이딩해서 사용
    • equals() : 해당 인스턴스를 매개변수로 전달받는 참조 변수와 비교하여 결과 반환
    • toString() : 인스턴스에 대한 정보를 문자열로 반환
    • clone() : 해당 인스턴스를 복제해, 새로운 인스턴스를 생성하고 반환한다.
      • clone()은 필드만 복제하기 때문에, 배열같은 경우 오버라이딩을 통해 재정의해야한다.

 

Constructor 상속은 어떻게?

  • 기본적으로 생성자는 상속되지 않는다.
  • super와 super()
    • super : 현재 사용하는 객체의 referenct -> type이 상위 타입
      • this와 마찬가지로, 부모 클래스 멤버와 자식 클래스 멤버 이름이 같을 경우 super 키워드로 구별
    • super() : 상위 클래스의 생성자 호출
      • 부모 클래스의 멤버를 초기화하려면, 자식 클래스의 생성자에서 부모 클래스의 생성자까지 호출해야 한다.
      • 기본 생성자가 아닌 다른 생성자를 선언했으면, 컴파일러가 자동으로 부모 클래스의 멤버를 초기화줄 수 없다.
      • 생성자를 따로 선언하고, 기본 생성자가 없으면 자식 클래스에서 super()호출 시 에러 발생
      • 되도록 기본 생성자까지 명시적으로 선언해주기
class Parent {
    int a;
    Parent() { a = 10; }
    Parent(int n) { a = n; }
}
 
class Child extends Parent {
    int b;
    Child() {
        super();
        b = 20;
    }

 

 

동적 바인딩(dynamic binding)

  •  객체에 대한 type이 상위 타입이라 할지라도, 만약 override된 method에 하위에 존재한다면 overriding 된 method를  사용
  • 다형성을 사용하여 메소드를 호출할 때 발생하는 현상
  • 실행 시간(runtime), 파일을 실행하는 시점에 결정된다.
  • 실제 참조 객체는 서브 클래스 -> 서브 클래스 메소드 호출

정적 바인딩(static binding)

  • 컴파일(compile) 시간에 의해 결정된다.
  • 변수의 타입이 super 클래스여서 super클래스의 메소드 호출
class SuperClass {

    // static method
    static int staticCall(String msg){
        System.out.println(msg);
        return 100;
    }

    // fields
    int a = staticCall("1번입니다."); // 4번째 출력 : line 3 실행
    static int b = staticCall("2번입니다."); // 1번째 출력
    // static : 프로그램 시작 시 최초에 한 번 생성만 되고 초기화된다.
    // 인스턴스를 생성하지 않아도 바로 사용 가능

    // constructor
    public SuperClass(){  // 생성자 전에 필드 공간이 만들어 져야 한다. -> line 10
        staticCall("3번입니다."); // 5번째 출력 : 필드 공간 생성 후 실행 -> line 23 복귀
    }

    // constructor overloading
    public SuperClass(int i){
        this(); // 자신의 클래스가 가지고있는 다른 생성자를 호출 -> line 16
        staticCall("4번입니다."); // 6번째 출력 -> line 40으로 복귀
    }

    // method
    public void myFunc(){
        System.out.println("5번입니다."); // -> 9번째 출력
    }
}

class InheritanceTest extends SuperClass {
    // fields
    int c = staticCall("6번입니다."); // -> 7번째 출력 -> line 40 복귀
    static int d = staticCall("7번입니다."); // 2번째 출력

    // constructor
    public InheritanceTest(){ // 객체 생성
        super(100); // 상위 클래스의 생성자(100) 호출 -> line 21
        staticCall("8번입니다."); // 생성자 전에 필드 공간 생성 -> line 34 -> 8번째 출력
        super.myFunc(); // -> 상위 클래스의 myFunc 메소드 실행
    }

    @Override
    public void myFunc(){
        System.out.println("9번입니다."); // 10번째 출력
    }

    public static void main(String[] args) {
        System.out.println("10번입니다."); // 3번째 출력 : static -> main 실행
        SuperClass obj = new InheritanceTest(); // 객체 생성 -> line 38
        obj.myFunc(); // obj의 타입 SuperClass 이지만 동적바인딩으로 line 45

        // 객체에 대한 type이 상위 타입이라 할지라도
        // 만약 override된 method에 하위에 존재한다면
        // method는 overriding 된 method를 사용한다.
        // 동적 바인딩(dynamic binding)
    }
}

// 2->7->10->1->3->4->6->8->5->9

 

abstract 제어자

  • 추상 메소드를 만들 때 사용

 

추상 클래스(Abstract Class)

  • 추상 메소드가 1개라도 클래스 내부에 존재하면, 해당 클래스는 추상 클래스
  • 추상 메소드는 선언만 -> 메소드가 하는 일이 정의되지 않았다.
  • 추상 클래스는 인스턴스를 생성할 수 없다.
  • 자식 클래스에서 오버라이딩 해서 사용
public abstract class UpperClass {
    // field
    String name;
    int age;

    // method
    public abstract void printAll();
        // abstract method
}

class subClass extends UpperClass{

    @Override
    public void printAll(){
        // TODO Auto-generated method stub
    }
}

 

인터페이스(Interface)

  • 추상 클래스의 특별한 케이스
  • 모든 메소드가 추상 메소드이다.
  • 모든 필드는 public static final -> 안에 들어가 있는 필드는 모두 상수
    • public 어디에서나 접근 가능, static 전역, final 상수화
  • 추상 메소드와는 다르게, 다중 implements 가능
interface myInterface {
    // fields
    //public static final
    int kk=0;
    String aa = "Hello";

    // abstract methods
    public abstract void printAll();
    public abstract void myPrint();
}

class MyClass implements myInterface{
    // 상속이랑 같은 개념을 사용한다.
    // is - A 관계 성립!!
    // 인터페이스 안에 있는 값들을 확장해서 새로운 클래스(인터페이스)를 구현한다는 의미
    // 인터페이스 안에서 내가 구현한 해당 인터페이스의 모든 내용들을 오버라이딩 해줘야한다.
    // generate 사용 -> 오버라이드 메소드
    @Override
    public void printAll() {

    }

    @Override
    public void myPrint() {

    }
}

 

 

인터페이스, 클래스 구현 예시

abstract class Myclass extends Human implements Aniaml
// Myclass에서 Human 상속, Animal interface를 구현한다는 뜻

abstract class Myclass extends Human implements Aniaml, MyInterface
// 인터페이스는 다중 구현 가능

형변환(type conversion)

  • 묵시적 형변환 : (컴파일러) 자동 변환
    • double num1 = 10
    • System.out.print(num1) // 10.0
  • 명시적 형변환(type casting) : 캐스트 연산자(괄호) 사용
    • int result = 1 / 4; // int(0.25) = 0
    • System.out.print(double(result)) // 0.0

 

삼항 연산자(ternary operator)

  • true : 반환값1
  • false : 반환값2
조건식 ? 반환값1 : 반환값2

 

 

루프와 제어

  • continue : 루프 내에서 사용하여 해당 루프의 나머지 부분은 건너 뛰고, 바로 다음의 조건식으로 넘어가게 해준다.
  • break : 루프 내에서 해당 반복문을 완전 종료

 

static 제어자

  • 해당 변수를 클래스 변수로 만들어준다.
  • 메소드에 사용하면 해당 메소드를 클래스 메소드로 만들어준다.
  • 초기화 블록에도 사용 가능
  • 프로그램 시작 시 최초에 한 번 생성만 되고 초기화된다.
  • 인스턴스를 생성하지 않아도 바로 사용 가능
  • 해당 클래스의 모든 인스턴스가 공유

 

클래스의 필드(field)

  • 필드 : 클래스에 포함된 변수(variable)
  • 선언 위치에 따라 구분된다.
  • 1. 클래스 변수(static variable) : 클래스 영역에서 static 키워드를 가지는 변수
    • 해당 클래스의 모든 인스턴스가 공유해야 하는 값을 유지하기 위해 사용
  • 2. 인스턴스 변수(instance variable) : static 키워드를 가지지 않은 변수
    • 인스턴스마다 가져야하는 고유의 값을 유지하기 위해 사용
  • 3. 지역 변수(local variable) : 메소드나 생성자, 초기화 블록 내에 위치한 변수
  • 클래스 변수 + 인스턴스 변수 : 컴파일러가 자동으로 초기화
  • 지역 변수는 초기화하지 않으면 오류 발생
  • 필드의 초기화 
    • 1. 명시적 초기화 : 필드 선언과 동시에 초기화
    • 2. 생성자를 이용한 초기화 : 객체 생성과 동시에 필드 초기화
      • 인스턴스 생성 전까지 필드 초기화 불가능
    • 3. 초기화 블록을 이용한 초기화 (초기화 블록 : main 메소드 호출 이전에 실행)
      • 초기화 블록은 생성자 보다 먼저 호출
      • 1. 인스턴스 초기화 블록 : 생성자와 거의 차이 없어서 잘 사용 X
      • 2. 클래스 초기화 블록 : 처음 메모리에 로딩될 때 단 한 번만 실행 
        • 필드의 초기화 순서
        • 클래스 변수 : 기본값 -> 명시적 초기화 -> 클래스 초기화 블록
        • 인스턴스 변수 : 기본값 -> 명시적 초기화 -> 인스턴스 초기화 블록 -> 생성자
class Car {
    static int modelOutput; // 클래스 변수
    String modelName;       // 인스턴스 변수
 
    void method() {
        int something = 10; // 지역 변수
    }
}
변수 생성 시기 소멸 시기 저장 메모리 사용 방법
클래스 변수 클래스가 메모리에 올라갈 때 프로그램 종료 메소드 영역 클래스 이름.변수이름
인스턴스 변수 인스턴스가 생성될 때 인스턴스 소멸 힙 영역 인스턴스이름.변수이름
지역 변수 블록 내에서 변수 선언문이 실행될 때 블록을 벗어날 때 스택 영역 변수이름
public class MyClass {
    // field
    int aaa;
    static int bbb = staticCall();

    // default constructor : class 확장성과 관계성이 있음.
    public MyClass(){

    }

    // static block
    static {
        // main이 실행되기 직전에 다른 library 같은 것들을 loading할 필요가 있을 때 사용한다.
        System.out.println("static block");
    }

    static int staticCall(){
        System.out.println("호출되었어요!");
        return 100;
    }

    public static void main(String[] args){
        System.out.println("main 호출");
    }
}

 

 

클래스 메소드와 인스턴스 메소드

  • 1. 클래스 메소드(static method) : static 키워드를 가지는 메소드
    • 인스턴스를 생성하지 않아도 바로 사용 가능
    • 메소드 내부에서 인스턴스 변수를 사용할 수 없다.
  • 2. 인스턴스 메소드(instance method) : static 키워드가 없는 메소드
class Method {
    int a = 10, b = 20;                            // 인스턴스 변수
    int add() { return a + b; }                    // 인스턴스 메소드

    static int add(int x, int y) { return x + y; } // 클래스 메소드
}
 
public class Member02 {
    public static void main(String[] args) {
        System.out.println(Method.add(20, 30)); // 클래스 메소드의 호출
        Method myMethod = new Method();         // 인스턴스 생성
        System.out.println(myMethod.add());     // 인스턴스 메소드의 호출
    }
}

ouput 
50
30

this와 this()

  • this : 현재 사용하는 객체의 reference (자기자신 참조)
    • 생성자의 매개변수 이름과 인스턴스 변수를 구분하기 위해 사용한다.
    • this를 사용해서 인스턴스 변수에 접근한다.
  • this() : 자신의 클래스가 가지고 있는 다른 생성자 호출 
    • 생성자 내부에서만 사용 가능
class Car {
    private String modelName;
    private int modelYear;
    private String color;
    private int maxSpeed;
    private int currentSpeed;
 
    Car(String modelName, int modelYear, String color, int maxSpeed) {
        this.modelName = modelName;
        this.modelYear = modelYear;
        this.color = color;
        this.maxSpeed = maxSpeed;
        this.currentSpeed = 0;
    }
    ...
}
    Car() {
        this("소나타", 2012, "검정색", 160); // 다른 생성자를 호출함.
    }

상수(constant)

  • final 키워드 사용
    • final은 다른 주소를 가리킬 수 없는 것이기 때문에
    • reference type의 변수에서는 그 대상 인스턴스 객체 값 수정 가능
    • final한 참조 타입 변수는 다른 주소를 가리킬 수 없을 뿐, 그 대상 인스턴스 객체의 값을 수정하지 못한다는 의미가 아니다.
    • final 배열은 값 변경, 정렬 가능
  • final int score = 00;
  • 선언과 동시에 초기화 되어야 한다.
  • 상수는 전부 대문자 사용한다 + Snake notation(_ 사용)
    • final int MY_SCORE=100;
  • 1. 변수 앞에 붙으면 상수 처리
  • 2. class 앞에 붙으면 상속 불가능
  • 3. method 앞에 붙으면 오버라이딩 불가능
final class Car { // 이 클래스는 상속을 통해 서브 클래스를 생성할 수 없음.
    final int VAR; // 이 필드는 상수화되어 값을 변경할 수 없음.
    final void brake() { // 이 메소드는 오버라이딩을 통해 재정의할 수 없음.
        final double MAX_NUM = 10.2; // 이 지역 변수는 상수화되어 값을 변경할 수 없음.
    }
}

 

String

  • String class : reference type 
  • primitive type 처럼 사용 가능
// 실행 fn + command + shift + f11
public class Student{
	// fields (변수)
	String stuName; // String -> class -> reference Type -> 문자
	String stuNum; // 학번(00231123) : 연산이 필요하면 int, 아니면 사용하기 편한 String
	int stuAge; // 나이 - 숫
	
	// methods (함수)
	public static void main(String args[]) {
		System.out.println("hello");
	}
}

 

StringBuffer, String, StringBuilder

  • String : 힙 영역에 있는 String Constant pool에 문자열이 저장된다. += 연산을 통하여 새로운 값으로 정의될 때, 기존의 값을 메모리에 유지하고 새로운 메모리 주소에 저장된다.
    • string은 immutable하기 때문에 멀티스레드 환경에서 안전성 보장
  • StringBuffer : 내부적으로 버퍼(buffer)라고 하는 독립적인 공간을 가진다. append를 통하여 새로운 값으로 정의되면, 기존의 값이 있는 주소에 버퍼에 있는 값을 메모리에 저장한다.
    • method별로 동기화 지원 -> 멀티스레드 환경에 적합
  • StringBuilder : StringBuffer와 내부 동작은 동일하지만, 동기화 보장 X
@Override
@IntrinsicCandidate
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

@Override
@IntrinsicCandidate
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

 

String 생성 방식 

  • 1. new 연산자
  • 2. 리터럴
    • String Constant pool(상수 풀)
      • Java 8부터 Perm(Permanent Generation) 영역에서 Heap 영역으로 이동
      • Java 8부터 Perm 영역 삭제 후 MetaSapce 영역이 대신하고 있다.
      • Perm 영역은 실행 시간(Runtime)에 가변적으로 변경할 수 없는 고정된 사이즈이기 때문에 intern 메소드 호출은 저장할 공간이 부족하게 만들 수 있었다. -> out of memory
        • intern() : String 클래스의 메소드. String과 동등한 객체가 이미 상수풀에 존재하면 그 객체를 그대로 리턴한다.
      • heap 영역으로 들어가게 된 상수풀은 Garbage Collection의 대상이 되었다.

 

+ Recent posts