멀티캠퍼스 백엔드 과정 16일차[6월 22일] - JAVA 인터페이스, 예외처리

GoldenDusk·2023년 6월 28일
0
post-thumbnail

인터페이스(Interface)

1. 인터페이스 역할

인터페이스

  • 두 장치를 연결하는 접속기술
  • 두 장치를 서로 다른 객체로 본다면, 두 객체를 연결하는 역할
  • 다형성 구현에 주된 기술

사용이유

  • 두 장치가 직접적으로 연결되어 있다면… 하나의 객체를 교체(업데이트)한다고 하면 두 객체 모두 수정해야하기 때문에 번거롭다.
  • 인터페이스를 연결해두면 교체하는 한 개만 수정하면 되기 때문에 훨씬 좋다.

💡 객체 A는 인터페이스를 통해서 객체 B를 사용 할 수 있다.
객체 A가 인터페이스의 메소드를 호출하면 인터페이스는 객체 B의 메소드를 호출하고 그 결과를 받아 객체A로 전달해준다.

인터페이스를 코드화시 알려주자면

인터페이스로 구현 객체를 사용하려면 인터페이스 변수로 선언하고, 구현객체를 대입하여 사용
인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입되면 구현 객체의 번지를 저장한다.
구현객체가 대입되면, 인터페이스 변수로 추상메소드를 호출할 수 있게 된다.
이때, 어떤 구현 객체가 대입되었는지에 따라 실행 내용이 달라진다.

2. 인터페이스와 구현 클래스 선언

인터페이스 선언

  • 인터페이스 선언은 class 키워드 대신 interface 키워드 사용
  • 접근 제한자로는 클래스와 마찬가지로 같은 패키지 내에서만 사용 가능한 default,
    패키지와 상관없이 사용하는 public을 붙일 수 있음
  • 고정값만 들어갈 수 있다.
public interface 인터페이스명{
//public 상수 필드
//public 추상 메소드
//public 디폴트 메소드
//public 정적 메소드
//private 메소드
//private 정적 메소
}

구현 클래스 선언

  • 인터페이스에 정의된 추상 메소드에 대한 실행 내용이 구현
  • implements 키워드는 해당 클래스가 인터페이스를 통해 사용할 수 있다는 표시이며,
    인터페이스의 추상 메소드를 재정의한 메소드가 있다는 뜻
public class B implements 인터페이스명{}

변수 선언과 구현 객체 대입

  • 인터페이스는 참조 타입에 속하므로 인터페이스 변수에는 객체를 참조하고 있지 않다는 뜻으로
    null을 대입할 수 있음
RemoteControl rc;
RemoteControl rc=null;
  • 인터페이스를 통해 구현 객체를 사용하려면, 인터페이스 변수에 구현 객체의 번지를 대입해야 함
rc=new Tv();

실습

public interface RemoteControl //인터페이스 클래스
{
	public void trunOn();
	public void turnOff();
}
public class Tv implements RemoteControl // 두 객체 중 1개
{

	@Override
	public void trunOn()
	{
		System.out.println("TV를 켭니다.");

	}

	@Override
	public void turnOff()
	{
		System.out.println("TV를 끕니다.");

	}

}
public class Radio implements RemoteControl // 두 객체 중 1개
{

	@Override
	public void trunOn()
	{
		System.out.println("라디오를 켭니다.");
		
	}

	@Override
	public void turnOff()
	{
		System.out.println("라디오를 끕니다.");
		
	}

}
public class RemoteControlEx
{

	public static void main(String[] args) //실행 클래스
	{
		RemoteControl rc;
		
		rc = new Tv(); 
		rc.trunOn();
		rc.turnOff();
		
		rc=new Radio(); // 부품만 교체
		rc.trunOn();
		rc.turnOff();
	}

}

3. 상수필드

상수 필드

  • public static final 특성을 갖는 불변의 상수 필드 값을 멤버로 가진다.
    • 인터페이스의 선언된 필드는 모두 public static fianl 특징을 갖는다.
    • 상수명은 대문자로 작성하되, 서로 다른 단어로 구성시 _로 연결하는 것을 권장

4. 추상메소드

추상 메소드

  • 리턴 타입, 메소드명, 매개변수만 기술되고 중괄호 { }를 붙이지 않는 메소드
  • public abstract를 생략하더라도 컴파일 과정에서 자동으로 붙음
  • 추상 메소드는 객체 A가 인터페이스를 통해 어떻게 메소드를 호출할 수 있는지 방법을 알려주는
    역할

5. 디폴트 메소드

디폴트 메소드

  • 인터페이스에는 완전한 실행 코드를 가진 디폴트 메소드를 선언할 수 있음
  • 추상 메소드는 실행부(중괄호 { })가 없지만 디폴트 메소드는 실행부 있음. default 키워드가 리턴
    타입 앞에 붙음
  • interface : RemoteControl
//디폴트 메소드 : 인터페이스에 넣을 수 있는 완전히 실행코드를 가진 유일한 메소드
	default void setMute(boolean mute) {
		if(mute) {
			System.out.println("무음 처리합니다.");
			setVolume(MIN_VOLUME);
		}
		else {
			System.out.println("무음 기능 해제합니다.");
		}
	}
  • 인터페이스와 연결된 Tv class
//디폴트 메소드 재정의 : default 키워드를 삭제 후 public로 변경 후 재정의
	public void setMute(boolean mute) {
		if(mute) {
			this.memoryVolume = this.volume; //해제 후 복구해야 하기 때문
			System.out.println("무음 처리합니다.");
			setVolume(MIN_VOLUME);
		}
		else {
			System.out.println("무음 기능 해제합니다.");
			setVolume(this.memoryVolume); //mute가 false일 때 원래 볼륨으로 복원
		}
	}
  • 실행 클래스 사용법
rc = new Tv();
rc.setMute(true); //디폴트 메소드 호출
rc.setMute(false);

6. 정적 메소드

  • 구현 객체가 없어도 인터페이스만으로 호출할 수 있음
  • 선언 시 public을 생략하더라도 자동으로 컴파일 과정에서 붙음
static 리턴타입 메소드명(매개변수, ..){...}
  • 정적 실행부를 작성할 때 상수 필드를 제외한 추상 메소드, 디폴트 메소드, private 메소드 등을
    호출할 수 없음
  • interface : RemoteControl
// 정적 메소드 : 추상메소드와 디폴트 메소드는 구현 객체가 필요하지만, 정적 메소드는 구현객체가 없이도 인터페이스만으로 호출 가능
	static void changeBattery() {
		System.out.println("리모콘 건전지를 교환해 주세요."); //순수하게 RemoteControl에 붙어있음 => 직접호출 가능
	}
  • 실행 클래스 사용법
RemoteControl.changeBattery();

실습 2(실습 1에 추가)

  • 인터페이스 클래스
public interface RemoteControl //인터페이스 클래스
{
	//public static final 상수
	int MAX_VOLUME = 10;
	int MIN_VOLUME = 0; //상수선언

	// 추상메소드 : 바디{}가 없는 메소드 선언부만 가지고 있는 메소드
	public void trunOn();
	public void turnOff();
	public void setVolume(int volume); //추상 메소드
	
	//디폴트 메소드 : 인터페이스에 넣을 수 있는 완전히 실행코드를 가진 유일한 메소드
	default void setMute(boolean mute) {
		if(mute) {
			System.out.println("무음 처리합니다.");
			setVolume(MIN_VOLUME);
		}
		else {
			System.out.println("무음 기능 해제합니다.");
		}
	}
	
	// 정적 메소드 : 추상메소드와 디폴트 메소드는 구현 객체가 필요하지만, 정적 메소드는 구현객체가 없이도 인터페이스만으로 호출 가능
	static void changeBattery() {
		System.out.println("리모콘 건전지를 교환해 주세요."); //순수하게 RemoteControl에 붙어있음 => 직접호출 가능
	}
	
	// private 메소드 : 구현 객체가 필요한 메소드
	// private 정적 메소드 : 구현 객체가 필요없는 메소드
	// 인터페이스 내에서 private 메소드의 용도는 디폴드와 정적메소드들의 중복 코드를 줄이기 위해 사용
	
}
  • TV
public class Tv implements RemoteControl // 두 객체 중 1개
{
	
	private int volume;

	@Override
	public void trunOn()
	{
		System.out.println("TV를 켭니다.");

	}

	@Override
	public void turnOff()
	{
		System.out.println("TV를 끕니다.");

	}
	@Override
	public void setVolume(int volume)
	{
		if(volume>RemoteControl.MAX_VOLUME) {
			this.volume = RemoteControl.MAX_VOLUME;
		}
		else if(volume<RemoteControl.MIN_VOLUME) {
		    this.volume = RemoteControl.MIN_VOLUME;
		}
		else {
			this.volume=volume;
		} //인터페이스 상수 필드를 이용해서 volume 필드의 값을 제한
		System.out.println("현재 Tv의 볼륨은 "+this.volume);
	}
	
	private int memoryVolume;
	
	//디폴트 메소드 재정의 : default 키워드를 삭제 후 public로 변경 후 재정의
	public void setMute(boolean mute) {
		if(mute) {
			this.memoryVolume = this.volume; //해제 후 복구해야 하기 때문
			System.out.println("무음 처리합니다.");
			setVolume(MIN_VOLUME);
		}
		else {
			System.out.println("무음 기능 해제합니다.");
			setVolume(this.memoryVolume); //mute가 false일 때 원래 볼륨으로 복원
		}
	}
	}
  • Radio
public class Radio implements RemoteControl // 두 객체 중 1개
{
	private int volume;

	@Override
	public void trunOn()
	{
		System.out.println("라디오를 켭니다.");
		
	}

	@Override
	public void turnOff()
	{
		System.out.println("라디오를 끕니다.");
		
	}

	@Override
	public void setVolume(int volume)
	{
		if(volume>RemoteControl.MAX_VOLUME) {
			this.volume = RemoteControl.MAX_VOLUME;
		}
		else if(volume<RemoteControl.MIN_VOLUME) {
		    this.volume = RemoteControl.MIN_VOLUME;
		}
		else {
			this.volume=volume;
		} //인터페이스 상수 필드를 이용해서 volume 필드의 값을 제한
		System.out.println("현재 Radio의 볼륨은 "+this.volume);
	}
		
	}
  • 정수기
public class WaterMachine implements RemoteControl
{
	private int volume;

	@Override
	public void trunOn()
	{
		System.out.println("정수기를 켭니다.");
		
	}

	@Override
	public void turnOff()
	{
		System.out.println("정수기를 켭니다.");
		
	}

	@Override
	public void setVolume(int volume)
	{
		if(volume>RemoteControl.MAX_VOLUME) {
			this.volume = RemoteControl.MAX_VOLUME;
		}
		else if(volume<RemoteControl.MIN_VOLUME) {
		    this.volume = RemoteControl.MIN_VOLUME;
		}
		else {
			this.volume=volume;
		} //인터페이스 상수 필드를 이용해서 volume 필드의 값을 제한
		System.out.println("현재 WaterMachine의 볼륨은 "+this.volume);
	}
	

}
  • 실행클래스
public class RemoteControlEx
{
	 // 인터페이스로 구현 객체를 사용하려면 인터페이스 변수로 선언하고, 구현객체를 대입하여 사용
    //	인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입되면 구현 객체의 번지를 저장한다.
   //	구현객체가 대입되면, 인터페이스 변수로 추상메소드를 호출할 수 있게 된다.
  // 이때, 어떤 구현 객체가 대입되었는지에 따라 실행 내용이 달라진다. 

	public static void main(String[] args) // 실행 클래스
	{
		RemoteControl rc;

		rc = new Tv();
		rc.trunOn();
		rc.setVolume(4);
		rc.setMute(true); //디폴트 메소드 호출
		rc.setMute(false);
		rc.turnOff();
		RemoteControl.changeBattery();
		System.out.println();

		rc = new Radio(); // 부품만 교체
		rc.trunOn();
		rc.setVolume(7);
		rc.turnOff();
		System.out.println();
		
		rc = new WaterMachine();
		rc.trunOn();
		rc.setVolume(8);
		rc.turnOff();
		System.out.println();

		// 상수는 구현 객체와 관련이 없는 인터페이스 소속 멤버이므로 바로 접근해서 상수값을 읽을 수 있다.
		System.out.println("리모콘의 최대 볼륨은 " + RemoteControl.MAX_VOLUME);
		System.out.println("리모콘의 최소 볼륨은 " + RemoteControl.MIN_VOLUME);

	}

}

7. private 메소드

private 메소드

  • 인터페이스의 상수 필드, 추상 메소드, 디폴트 메소드, 정적 메소드는 모두 public 접근 제한을 가짐
  • public을 생략하더라도 항상 외부에서 접근이 가능
  • 인터페이스에 외부에서 접근할 수 없는 private 메소드 선언도 가능
  • private 메소드 : 구현 객체가 필요한 메소드
  • private 정적 메소드 : 구현 객체가 필요없는 메소드
  • 인터페이스 내에서 private 메소드의 용도는 디폴드와 정적메소드들의 중복 코드를 줄이기 위해 사용

8. 다중 인터페이스와 인터페이스 상속

실습 코드를 보면서 이해

  • interface
public interface RemoteControl
{
	void turnOn();
	void turnOff();
}
public interface Searchable
{
	void search(String url);
}
  • 두 개의 인터페이스를 합친 totalinterface 이로 인해 한 번에 묶어서 두 인터페이스의 메서드를 모두 사용 가능하게 됨
public interface totalinterface extends RemoteControl, Searchable //인터페이스의 다중 상속 허용
{ // 만약 필요가 없다면 부품 빼듯이 extends 뒤에서 빼거나 수정시 업데이트 하면 됨
	//사용자가 보는 인터페이스 코드 느낌 => 아무것도 볼 수 없음
	// 스텁, 인터페이스만 가지고 있음

} 
  • 객체
public class SmartTv implements totalinterface
{

	@Override
	public void search(String url)
	{
		System.out.println(url);
		
	}

	@Override
	public void turnOn()
	{
		System.out.println("스마트 티비 켭니다.");
		
	}

	@Override
	public void turnOff()
	{
		System.out.println("스마트 티비 끝니다.");
		
	}
	
}
  • 실행클래스
public class MultiInterfaceEx
{

	public static void main(String[] args)
	{
		// 두 개의 인터페이스를 같이 묶은
		totalinterface ti=new SmartTv();
		
		// RemoteControl 인터페이스 변수 선언 및 구현 객체(SmartTv) 대입
		RemoteControl rc = new SmartTv();
		
		// RemoteControl 인터페이스에 선언된 메소드만 호출 해 주세요
		 rc.turnOn();
		 rc.turnOff();
		 
		 System.out.println("===================");
		 ti.turnOn();
		 ti.turnOff();
		 ti.search("우주하마 유투브, 혜안 유투브");
		 
		 System.out.println("==================");
		 //Searchable 인터페이스 변수 선언하고 구현 객체 대입
		 Searchable s = new SmartTv();
		 
		 // Searchable 인터페이스에 선언된 추상 메소드만 호출해 주세요
		 s.search("htts://www.youtube.com");

	}

}

타입변환

강제 타입변환

  • 캐스팅 기호를 사용해서 인터페이스 타입을 구현 클래스 타입으로 변환시키는 것
구현 클래스 변수 = (구현클래스) 인터페이스 변수
  • 구현 객체가 인터페이스 타입으로 자동 변환되면, 인터페이스에 선언된 메소드만 사용 가능

예외처리

1. 예외와 예외 클래스

예외와 에러

  • 에러(error) : 컴퓨터 하드웨어 고장으로 인해 생기는 응용프로그램 오류
  • 예외(Exception)
    • 잘못된 사용(논리적, 명령어) 또는 코딩으로 인한 오류, 개발자가 바로잡을 수 있는 오류
    • 예외가 발생하면 프로그램은 바로 종료되어 불완전 종료가 될 수 있음
    • 하지만 우리는 프로그램의 안전한 종료를 위해 예외처리를 하여 프로그램을 안전하게 종료하거나 계속 실행상태를 유지하게 만들 수 있다.
    • 에러가 달리 예외 처리를 통해 계속 실행 상태를 유지할 수 있음
    • 에러 이외의 모든 에러
  • 일반 예외(Exception) : 컴파일러가 예외 처리 코드 여부를 검사하는 예외
if(n2==0) {
			System.out.println("0으로 나눌 수 없어요");
		}
		else {
			int result = n1/n2;
			System.out.println(result);
		} //일반 예외처리
		System.out.println("프로그램의 끝");
	}
  • 실행 예외(Runtime Exception) : 컴파일러가 예외 처리 코드 여부를 검사하지 않는 예외

예외 처리 코드

예외 처리

  • try-catch-finally블록은 생성자 내부와 메소드 내부에서 작성
  • 예외 발생 시 프로그램의 갑작스러운 종료를 막고 정상 실행을 유지할 수 있게 처리하는 코드
  1. **Exception은 ArithmeticException, NullPointerException 등의 부모라서 여러 개라고 해도 Exception e만으로 처리 가능**
  2. 예외처리 나열된 순차적으로 처리
//Exception handling 한다.
public class ExceptionEx01
{
	public static void main(String[] args) {
		int n1 = 5;
		int n2 = 0;
		int result=0;
		
		try{
			 result = n1/n2;
		}
		catch(ArithmeticException e){
			
			System.out.println("0으로 나눌 수 없어요");
		} 
		catch(Exception el) {
			System.out.println("예외 처리 완료");
		}
		finally{
		System.out.println("예외처리 되었습니다.");
	}
	System.out.println(result);
	
	System.out.println("프로그램의 끝");
}
}

예제 : NullPointerException

public class NullPointEx
{
	public static void printLength(String data)
	{
		int result = 0;
		try
		{
			result = data.length();
		} catch (NullPointerException e)
		{
			System.out.println(e.getMessage()); //예외 정보를 얻는 3가지 방법
			// System.out.println(e.toString());
			// e.printStackTrace()
		} catch(Exception e1)
		{
			System.out.println(e1.getMessage());
		}
		
		System.out.println("문자 수: " + result);
	}

	public static void main(String[] args)
	{

		System.out.println("프로그램 시작");
		printLength("[프로그램 시작]");
		printLength(null);
		System.out.println("프로그램 종료");
	}
}

예제 : ClassNotFoundException

public static void classNotfoundEx(String message) {
	
		try {
		Class.forName(message);
		System.out.println("자바표준 API에 "+message +" 클래스가 존재합니다.");
		}
		catch(ClassNotFoundException e) {
			//e.printStackTrace();
			System.out.println("표준 API에 없는 클래스입니다. ");
		}
}

public static void main(String[] args)
	{
		classNotfoundEx("java.lang.String2");
	}

예제 : ArrayIndexOutOfBoundsException와 NumberFormatException

public class ArrayIndexOutOfEx
{
	public static void arryEx(String[] array) {
		for(int i=0; i<array.length+1;  i++) {
			try {
			System.out.println(array[i]);
			int value = Integer.parseInt(array[i]);
			}
			catch(ArrayIndexOutOfBoundsException e) {
				System.out.println(e.getMessage());
			}
			catch(NumberFormatException e) {
				System.out.println(e.getMessage());
			}
		}
	}
	public static void main(String[] args) {
		String[] array = {"100", "200", "300", "1ooo"};
		arryEx(array);
		System.out.println("프로그램종료");
	}
}

2. 리소스 자동 닫기

리소스(Resource)

  • 데이터를 제공하는 객체
  • 사용하기 위한 것
    1. open
    2. processing
    3. close
  • 리소스를 사용하다가도 예외 발생시 안전하게 닫는 것이 중요하다.
  • try-with-resources 블록을 사용하면 예외 발생 여부 상관없이 리소스를 자동으로 닫아줌

예제

public class MyResource implements AutoCloseable
{
	private String name;
	
	public MyResource() {};
	public MyResource(String name) {
		this.name = name;
		System.out.println("[My Resource("+name+"]열기");
	}
	
	 public String read1() {
		 System.out.println("[My Resource("+name+"]열기");
		 return "100";
	 }
	 
	 public String read2() {
		 System.out.println("[My Resource("+name+"]열기");
		 return "abc";
	 }
	

	@Override
	public void close() throws Exception // throws:excetion한테 빨리 처리하라고 떠넘긴 것
	{
		System.out.println("[My Resource("+name+"]닫기");
	}

}
  • 실행 클래스
public class TryWithResourceEx
{

	public static void main(String[] args)
	{
		try(MyResource res = new MyResource("A")) {
			String data = res.read1();
			
		}catch (Exception e) {
			System.out.println("예외처리:"+e.getMessage());
		}
		System.out.println("===========");
		
		try(MyResource res = new MyResource("A")) {
			String data = res.read2();
			//NumberFormatException 발생 
			int value = Integer.parseInt(data);
			
		}catch (Exception e) {
			System.out.println("예외처리:"+e.getMessage());
		}
		System.out.println("===========");
		
		
		MyResource res1 = new MyResource("A");
		MyResource res2 = new MyResource("B");
		
		try(res1; res2){
			String data1 = res1.read1();
			String data2 = res2.read2();
		}catch (Exception e) {
			System.out.println("예외처리:"+e.getMessage());
		}
		
		

	}

}

3. 예외 떠넘기기

예외 떠넘기기

  • 메소드 내부에서 예외가 발생했을 때, try-catch 블록으로 처리가 기본
  • 메소드를 호출한 곳으로 예외를 떠넘길 수 있다.
  • 메소드 내부에서 예외 발생 시 throws 이용해 메소드 호출한 곳으로 예외 떠넘김
  • throws는 메소드 선언부 끝에 작성. 떠넘길 예외 클래스를 ,로 구분해 나열

예제 1 ClassNotFoundException

public class ThrowsEx
{
	public static void findClass() throws ClassNotFoundException{
		Class.forName("java.lang.String1"); //문제 발생시 예외처리 : java.lang.String1 예외가 없다면 메세지 안나옴
	}

	public static void main(String[] args)
	{
		try {
			findClass();
		}
		catch(ClassNotFoundException e) { // 여기에 예외를 떠넘김
			System.out.println("예외처리: "+e.getMessage());
		}

	}

}

예제 2

package javastudy1.day0622.exception;

public class ThrowsEx
{
	public static void findClass() throws Exception{
		Class.forName("java.lang.String1"); 
	}

	public static void main(String[] args) throws Exception //jvm에서 처리
	{
		try {
			findClass();
			}
		catch(Exception e) {
			System.out.println("예외처리:"+e.getMessage());
		}

	}

}

예제 3 계좌 출금 예금 느낌, InsufficientException

public class InsufficientException extends Exception
{
	public InsufficientException() {}
	public InsufficientException(String message) {
		super(message);
	}
}
  • 새로운 예외 발생시키기 위해 사용 : throw new~
throw new InsufficientException("잔고부족:"+(money-balance)+"금액 부족합니다.");
public class Account //은행계좌
{
	private long balance;
	
	public Account() {}
	
	public long getBalance() {
		return this.balance; //현재 잔고를 볼 수 있게
	}
	
	//입금 가능
	public void deposit(int money) {
		balance += money;
	}
	// 출금 기능 => 잔고보다 큰 금액은 출금할 수 없다.
	public void withdraw(int money) throws InsufficientException {
		if(balance < money) {
			throw new InsufficientException("잔고부족:"+(money-balance)+"금액 부족합니다.");
		}
		balance -= money;
	}
}
public class AccountEx
{

	public static void main(String[] args)
	{
		Account account = new Account();

		//예금하기 => 버튼으로 연결 가능		
		account.deposit(10000);
		System.out.println("총 예금액 :"+account.getBalance());
		
		//출금
		try {
		account.withdraw(5000); //바로 호출 불가 => exception으로 처리
		System.out.println("현재 잔고 :"+account.getBalance());
		account.withdraw(7000);
		}
		catch(InsufficientException e) {
			System.out.println(e.getMessage());
		}
	}

}

사용자 정의 예외

사용자 정의 예외

  • 표준 라이브러리에는 없어 직접 정의하는 예외 클래스
  • 일반 예외Exception의 자식 클래스로 선언, 실행 예외RuntimeException의 자식 클래스로 선언

예외 발생시키기

  • throw 키워드와 함께 예외 객체를 제공해 사용자 정의 예외를 직접 코드에서 발생 가능
  • 예외의 원인에 해당하는 메세지를 제공하려면 생성자 매개값으로 전달

회고

노션에 어느정도 정리 후 블로그에 올리는 것도 생각보다 번거롭다ㅠ 그래도 노션보다는 벨로그가 인덱스같은거 정리도 해주고 좋은듯.. 다만 이게 한 단원이 다음날까지 이어지는 경우도 있어서 그 단원 끝나고 정리하고 올리느라 바로 당일날 올리기가 불가한 것 같다.
이건 공부 정리이자 잊지 않기 위한 용도이고 알고리즘을 위한 파이썬이랑 개인적으로 공부할 것 좀 따로 찾아봐야겠다.

profile
내 지식을 기록하여, 다른 사람들과 공유하여 함께 발전하는 사람이 되고 싶다. gitbook에도 정리중 ~

0개의 댓글