객체지향 프로그래밍이란?
객체지향은 소프트웨어의 모든 구성원이 객체로 구성되며, 객체들의 관계성으로 프로그램이 작동되는 것을 뜻합니다. 한 마디로 말하자면 객체를 사용해서 코드를 짜는 것입니다. 자바, C++, C# 등이 객체지향 언어입니다. 그럼 객체는 무엇일까요?
객체란?
객체는 오브젝트입니다. 객체는 '데이터'와 '기능'을 가지고 있습니다. 다른 말로 표현해보자면 객체는 변수와 함수를 묶어놓은 것입니다. 여기서 말하는 함수란, 어떠한 오퍼레이션을 뜻합니다. 펑션 function 이라고 하기도 하고 메소드 method 라고도 합니다.
자바에서 new 하면 '객체화'됩니다. 예를 들어 Student 클래스로 객체를 생성하는 코드라면, Student s = new Student(); 와 같은 형태일 텐데요. 이때 클래스 Student의 주소값을 기억하는 방이 's' 입니다. 이것을 new 할 때 객체화됩니다.
클래스란?
클래스는 어떤 데이터와 어떤 기능을 가지고 있는지 설계 해둔 것입니다. 다시 말해 객체의 설계도라고 볼 수 있습니다. 클래스는 멤버변수를 선언하는 부분과 생성자 그리고 메소드가 적혀있는 부분이 있습니다. 그리고 클래스명은 첫글자를 대문자로 하기로 약속되어 있습니다.
클래스의 메소드가 적혀있는 부분은 예를 들어 public void 메소드명 (파라메터 ) 와 같은 형태입니다. 여기서 public 과 같은 부분은 제한자를 뜻합니다. 영어로는 modifiers 라고 하며 접근이나 사용에 대한 제한을 정의합니다. 퍼블릭이면 바깥에서 해당 메소드가 접근가능하다는 뜻입니다. 생성자의 경우 제한자는 하나만을 사용할 수 있습니다. 메소드는 제한자가 하나 이상일 수 있습니다.
참고로, 여러 클래스들이 어떤 관계를 가지고 있는지 나타내는 그림은 클래스 다이어그램이라고 합니다.
접근 제한자 Access Modifier 4가지
자바에서 사용하는 접근제한자는 4단계가 있습니다. protected 의 범위가 약간 헷갈릴 수 있으므로 주의하면 됩니다.
Universe | Subclass | Same Package | Same Class | |
public | O | O | O | O |
protected | O | O | O | |
(default) | O | O | ||
private | O |
Public이면 모든 곳에서 다 접근이 가능하다는 것이고 Private이면 같은 클래스 안에서만 접근할 수 있다는 뜻입니다.
사용 제한자 Usage Modifier 3가지
Static | 객체 생성없이 사용하고자 할 경우에 사용합니다. 원래는 객체를 new 해서 사용해야 하지만 static 인 경우 new 할 필요가 없습니다. |
Final | 변경할 수 없음을 의미합니다. 즉 상수에 사용합니다. |
Abstract | 추상 클래스 관련입니다. |
변수 라이프 사이클
변수를 두 가지 종류로 분류하면 멤버 변수와 로컬 변수가 있습니다.
먼저, 클래스 안에 정의되는 변수는 멤버 변수라고 합니다. 멤버 변수는 인스턴스 저장 공간인 힙에 생성됩니다. 로컬 변수는 스택에 생성되는 것과 차이점이 있습니다.
멤버변수는 static과 instance 로 나뉩니다. static 변수는 클래스가 메모리에 로딩될 때 생성됩니다. 이 때 메모리 할당이 이루어지고 자동으로 초기화 됩니다. static 변수는 단 한 번만 만들어져서, 클래스가 로드되어 있는 동안 두고 두고 공유해서 사용합니다. 그리고 클래스가 제거될 때 static 변수도 같이 제거됩니다.
static 하지 않은 멤버 변수는 instance 변수입니다. 인스턴스 객체 생성 시 자동으로 초기화 되고, 객체 제거 시에 제거됩니다. 초기화될때 default는 char타입과 정수형 타입은 0이고 실수형은 0.0 이며 boolean 타입은 false 입니다. String이나 레퍼런스 타입은 null로 초기화됩니다.
로컬 변수는 메소드나 생성자 내에서 정의되고 그 안에서만 사용하는 변수입니다. 연산 과정의 중간 값을 저장하는 공간인 스택에 저장됩니다. 메소드나 생성자 호출 시에 생성되었다가 해당 메소드가 종료되면 사라집니다. 자동으로 초기화 되지 않습니다. 그래서 주의해서 사용해야 합니다. 꼭 초기화를 해주도록 합시다.
JVM 메모리 구조: Java 메모리 관리
자바는 C나 C++과 달리 메모리를 개발자가 관리하지 않습니다. Garbage Collection 가비지 콜렉션 (이하 GC) 라는 쓰레드가 사용하지 않는 객체를 정리해줍니다. GC가 돌아가는 동안은 다른 쓰레드가 돌지 않아서 약간 느려집니다. 그래서 효율적으로 청소하는 것이 중요합니다. 구현한 벤더마다 버전마다 JVM의 메모리 관리 기법이 다르고 GC의 관리방법이 다르므로 성능이 다를 수 있습니다.
구현 로직은 모두 다르지만 그래도 공통적인 구조는 있습니다. 크게 분류해보자면 다섯 가지 입니다.
- HDD: 하드 디스크. 클래스 파일이 저장되어 있다.
- Class Loader: 클래스를 로딩한다. 하드 디스크에 있는 번역된 클래스 파일을 메모리로 읽어온다. 로더도 클래스다. 클래스 로더를 오버라이딩해서 HDD 말고 네트워크 등 다른 곳에서 받아오게 바꿀 수도 있다.
- Runtime data area (1) Class area: 클래스 정보, static 정보, method 영역. 메모리로 읽어온 클래스의 정보를 기억하는 곳이다. GC의 청소대상 공간이다.
- Runtime data area (2) Heap: 객체가 생성되는 공간. 클래스의 객체를 생성하여 기억하는 곳이다. GC의 청소대상 공간이다.
- Runtime data area (3) Stack: 프로그램 수행 시 로컬변수가 중간 결과를 저장하는 곳. 메소드 수행 시 마다 프레임이 할당되어 메소드 수행에 필요한 변수나 중간 결과 값을 임시 기억 하는 곳이다. 메소드 종료 시 할당되었던 메모리는 자동으로 제거된다. 그래서 스택은 청소가 필요없는 공간이다. GC의 청소대상 영역에서 제외되어 있다.

GC는 CPU가 한가하거나 메모리가 부족할 때 JVM에 의해서 자동으로 실행됩니다. 직접 호출도 가능합니다. System.gc() 를 통해 호출할 수 있는데 다만 바로 수행된다는 보장은 없습니다.
- 메모리 청소 방법: 방을 쪼개서 조그만 방에 모두 new된 객체를 넣고 사용하다가, 일정시간이 지나면 남을 것만 다른 방으로 옮겨가고 그 방 전체를 삭제한다.
객체 생성 시 내부 메모리 실행 순서
Member m1 = new Member();
- stack에 m1 변수 공간을 할당한다. 초기화 되지 않은 상태다.
- class loader에 member 클래스 로딩 요청을 보낸다.
- class loader는 멤버 클래스가 로딩되어 있지 않다면 로딩한다. 그리고 그 레퍼런스(주소값)을 임시로 저장(캐싱)한다. 만약 캐싱정보에 이미 있었던 클래스라면 로딩하지 않고 기존 주소값을 리턴한다.
- 로더가 메모 - 리에 클래스를 올리면 클래스 영역에 "원본의 정보를 기억하는" 클래스가 생기고, 그 클래스의 이름은 클래스다. 즉, 클래스라는 클래스 ( :Class )이다.
- 로딩된 멤버 클래스 정보를 통해 멤버 객체를 생성한다. (new한다.) (따라서 불필요한 new는 메모리 낭비다.)
- 멤버 클래스의 생성자를 수행한다.
- 생성된 멤버 객체의 주소값을 m1에 저장한다.
- 생성된 객체에 값을 할당한다.
- 객체의 메서드를 호출하게 된다면, 스택에 메서드를 수행할 공간을 할당하여 메서드를 수행하고 수행이 끝나면 할당된 메모리는 자동으로 제거된다. 메서드 호출 시 마다 스택에 공간이 만들어진다.
생성자 Constructor 란?
생성자는 자바일 경우 new 할 때 불려집니다. 객체 생성 시 자동 호출되고 객체 초기화 작업을 수행합니다. 즉 객체 내 변수를 초기화 한다는 이야기 입니다. 생성자는 클래스와 이름이 같고 리턴타입이 없습니다. 리턴타입은 반드시 없어야 합니다. 리턴타입이 있는 것은 메소드가 됩니다. 그리고 생성자는 객체 생성 시에 항상 호출되기 때문에 반드시 존재해야 합니다.
클래스의 생성자는 반드시 존재해야 하지만 직접 코드로 구현해주지 않아도 되는데 그 이유는 생성자가 단 하나도 없으면 컴파일 시에 컴파일러가 기본 생성자를 코드에 자동으로 삽입해주기 때문입니다. 다만 생성자를 따로 정의할 경우에는 기본 생성자를 반드시 명시적으로 코드에 적어주어야 합니다. 다른 개발자가 그 클래스를 상속받으려고 할 때 기본 생성자가 없으면 오류가 나기 때문입니다.
객체지향의 특징 4가지 中 상속(Inheritance)
상속이란 어떤 클래스가 다른 클래스의 성질을 물려받는 것을 말합니다. 어떤 클래스의 데이터/변수와 기능/메소드를 다른 클래스에서 사용할 수 있도록 한 것입니다. 그래서 일관성이 증가합니다.
어떤 클래스를 만들 때 처음부터 모든 것을 새로 만들 필요 없이, 다른 클래스로부터 상속을 받아서 조금만 수정을 가하면 원하는 클래스를 만들 수 있어서 재사용성이 높아집니다.
또한 공통적인 성질들을 여러군데 반복해서 만들 필요가 없어서, 나중에 한 군데만 수정해도 양쪽 모두에 자동으로 반영되도록 설계할 수 있는 것이지요. 즉 유지보수성이 높아집니다.
이렇게, 비슷한 속성과 기능을 가지고 있는 다른 클래스를 상속 받아서 새로운 클래스를 정의하는 것은 Specialization 의 개념입니다. Specialization 과 한 쌍을 이루는 개념에 Generalization 이라는 개념이 있습니다. 부모와 자식을 사람이라는 상위 개념으로 묶어서 이름, 나이 등의 공통적인 특성을 뽑아 모아서 Super Class 로 정의하는 것을 Generalization 이라고 합니다.
상속관계는 화살표로 나타냅니다. 화살표의 방향은 하위 클래스(상속받은 클래스)로부터 상위 클래스(상속해준 클래스)를 향하게 합니다. 다른 말로는 Is a 관계라고 합니다. Is a 관계 이외에 Has a 관계도 존재합니다. 객체지향 프로그래밍에서 Has a 관계는 composition(합성, 복합)이라고 합니다. 어떠한 객체가 다른 객체를 갖고 있을 때를 말합니다.
상속을 하면 상속받는 속성과 메소드를 선택적으로 상속받을 수 없습니다. 모든 속성과 기능을 상속받아야 합니다. 수정을 원하는 부분은 오버라이딩하면 됩니다. 오버라이딩은 다형성과도 관련이 있습니다. 아래쪽에서 자세히 다뤄볼 예정입니다.
Super 키워드와 This 키워드
상속 개념과 관련하여 자바에서 Super 와 This 라는 키워드를 알아야 합니다. Super는 현재 수행중인 객체가 상속받은 상위 객체의 레퍼런스를 갖는 레퍼런스 변수입니다. This는 자기 자신의 레퍼런스를 가리키는 레퍼런스 변수입니다.
만약 super 라는 키워드가 너무 생소하다고 해도 겁 먹을 필요 없습니다. 아래쪽에 한번 더 언급되어 있으니 그때 다시 살펴보겠습니다.
private 로 정의되어 있는 super class 의 member 는 상속은 되지만 접근할 수는 없습니다. 상속받은 클래스를 사용할 때 이 점을 주의해서 사용해야 합니다.
sub class 의 인스턴스 생성 시에 super class 인스턴스도 같이 생성이 됩니다. 그러므로 메모리에 존재하는 super type으로 변수를 선언할 수 있습니다. 이를 object polymorphism 이라고 합니다. 다형성에 관해서는 아래에서 설명했으니 참고하시면 되겠습니다.
super class 의 생성자는 sub class 의 생성자 내의 첫 라인에 super()로만 접근이 가능합니다. 이 내용 관련으로 바로 아래에 간단한 예시 코드를 넣어보았습니다.
// super 생성자 사용 예시
public class Student extends Human{
public Student();
public Student(String name, int age, String school){
super(name, age);
setSchool(school);
}
}
레퍼런스 형변환 Reference Type Casting
레퍼런스 타입의 변수들은 상속관계일 경우 묵시적으로 형변환이 허용됩니다. sub class 객체 안에는 항상 super class 객체가 같이 있기 때문에 super 객체 타입으로 sub 객체를 형변환 할 수 있습니다. 위의 코드를 활용하여 예를 들어본다면, Human 타입으로 Student 객체를 만들 수 있습니다. Human h1 = new Student(); 과 같은 식입니다.
단, 주의할 점은 자동 형변환에 의해서 변환된 슈퍼 타입의 변수는 서브 객체가 추가한 메서드는 수행할 수 없습니다. super 가 가지고 있는 member 만 사용가능하게 됩니다. 컴파일러는 super에 있는지를 먼저 체크하고, 실행할 때는 sub에 오버라이딩된 메소드가 있다면 오버라이딩된 메소드를 수행합니다. 다형성 효과를 위해서 입니다.
만약 원래의 타입인 sub class 타입에 있는 멤버/메소드를 사용하고 싶다면 명시적 형변환을 수행해주어야 합니다. Student stu1 = (Student) h1; 과 같은 코드를 말합니다. 단, 명시적 형변환을 수행하기 전에 서브 객체가 메모리에 있는지 존재여부를 확인해야 합니다. 그 내용을 확인할 수 있는 코드는 instanceof 키워드입니다. 아래 예시 코드처럼 사용하면 됩니다.
if ( h1 instanceof Student ) {
// 변수 instanceof 서브클래스객체타입
}
Single Inheritance
자바에서 클래스는 하나의 클래스만 상속받을 수 있습니다. 다중 상속으로 인한 문제점들이 발견된 바 있기 때문에 하나만 받도록 제한이 되었습니다. 이것을 Single Inheritance 라고 합니다. 만약 다중 상속이 필요하다면 Interface 를 사용하여 다중 상속의 효과를 얻을 수 있습니다. 인터페이스는 바로 다음에 이어지는 객체지향의 다른 특징인 추상화 개념과 연관이 되어 있습니다.
객체지향의 특징 4가지 中 추상화(Abstraction)
어떤 클래스가 다른 클래스를 상속하고 상속받는 관계일 때, 상속 관계를 정의하는 과정에서 두 클래스 간의 공통된 속성과 행위를 추출하게 될텐데요. 이것이 바로 객체지향에서의 추상화라는 개념입니다.
예를 들어 학교 관련 DB에서 학생 클래스와 선생님 클래스가 있을 때, 사람이라는 공통 분모를 뽑아내어 사람 클래스를 만드는 것을 추상화 과정으로 볼 수 있습니다. 이때 사람 클래스를 학생 클래스가 상속 받게 되고 선생님 클래스도 사람 클래스를 상속받게 될 것입니다. 학생 IS A 사람이라고 말할 수 있습니다. 사람 클래스는 이름이나 나이 같은 공통 속성들을 가지게 될 것이고, 공통 메소드로는 (정말 아무 예시나 들어보자면) 아프다 등의 행동이 가능할 것입니다.
객체지향의 특징 4가지 中 다형성(Polymorphism)
위의 예시를 조금 더 이어가 보겠습니다. 학생과 선생님은 사람 클래스를 통해 가지게 되는 공통된 속성과 메소드가 있지만 학생과 선생님은 차이가 있습니다. 예를 들어 아프다라는 공통된 메소드이지만 학생 클래스와 선생님 클래스에서 구현을 다르게 가져갈 수 있습니다. 학생은 양호실 가기, 조퇴증 받기 등이 있을 수 있고 선생님은 양호실 가기, 휴가 내기 등이 있을 수 있겠죠. 그렇게 다른점을 구현하는 것을 다형성이라고 합니다.
객체지향에서의 다형성은 '여러 클래스들이 동일한 이름의 오퍼레이션을 서비스하도록 하는 것'이라고 합니다. 실제 코드에서 다형성은 하나의 클래스 내부에 같은 이름의 오퍼레이션을 여럿 정의하거나, 상위 클래스의 오퍼레이션을 하위 클래스에서 다시 정의하는 것을 통해 구현할 수 있습니다.
다형성 관련 개념: 오버라이딩과 오버로딩의 차이점
글자가 비슷해서 헷갈리기 쉬울 수 있는 것이 오버로딩과 오버라이딩입니다. 둘 다 다형성 관련 이라는 점에서 공통점이 있습니다. 하지만 차이점을 알아야 합니다.
class Member {
int id;
String name;
public Member() {
// default constructor
}
public Member(String name) {
// another constructor // 오버로딩
this.name = name;
}
@Override
public String toString() {
// 오버라이딩해서 커스터마이징 가능!
return "투스트링~ "+super.toString();
}
}
public class Main {
public static void main(String[] args) {
Member m = new Member("test");
System.out.println(m.toString());
}
}
오버라이딩 Overriding
메소드 오버라이딩(Method Overriding)이란, 어떤 메소드가 상위 클래스에 존재하고 그 클래스를 상속받은 하위 클래스에서 해당 메소드를 다시 정의하는 것을 말합니다. @Override 라는 어노테이션을 메소드 위에 써주면 됩니다.
@Override 라는 어노테이션은 그 어노테이션 아래에 정의되어 있는 메소드가 오버라이딩 문법에 맞는지 컴파일러에게 체크를 요청하게 합니다.
예를 들어, 개발자가 어떤 클래스를 직접 만든 경우, 자바에서 그 클래스는 자동으로 Object 라는 상위 클래스를 상속받고 있는데, 그 Object 라는 클래스에는 toString 과 같은 메소드가 있습니다. Object 클래스에서 기본으로 정의되어 있는 toString 메소드를 개발자가 직접 만든 클래스에서 수정 없이 그대로 사용해도 됩니다. 그러나 만약 내가 만든 클래스에 좀더 최적화 되도록 toString 으로 나오는 내용을 바꿔주고 싶다면 위의 코드처럼 오버라이딩을 하는 것입니다.
오버라이딩을 할 때 지켜야 할 규칙이 있습니다. sub class에서 오버라이딩한 메소드는 super class 의 메소드 이름이 반드시 같아야 합니다. 또한 그 메소드의 return type, argument list도 반드시 같아야 합니다. 접근 제한자는 super class 의 메소드의 접근 제한자와 같거나, 그것보다 더 넓은 범위를 가져야 합니다.
자바에서 This 와 Super
참고로 위 코드에서 super.toString() 부분은 'Member@15db9742'과 같은 주소값을 프린트하게 됩니다.
여기서 Super라는 키워드는 자식 클래스에서 부모 클래스를 참조할 때 사용합니다. 만약 부모 클래스와 자식 클래스의 변수 이름이 같은 경우에 Super를 붙여서 구분해서 사용할 수 있습니다. Super 는 객체 생성 시에 자동으로 생성이 됩니다.
Super 와 비슷한 것으로 this가 있습니다. 이 This 는 간단히 말하자면 자기 자신을 뜻합니다. 현재 수행 중인 객체의 레퍼런스 정보가 this 변수에 담기게 됩니다.
이 this 레퍼런스 변수도 객체 생성 시에 그 객체의 레퍼런스를 저장하기 위한 용도로 자동으로 생성이 되고, 메서드 수행 시에 이 this 를 사용할 수 있습니다. 예를 들어, 생성자 또는 메서드 내에서 변수를 사용하는 상황에서, JVM은 먼저 로컬변수가 존재하는지 확인하고 로컬에 그 변수가 없으면 this 가 가리키고 있는 객체에서 해당 변수를 찾아서 사용합니다.
this는 " this( 파라메터 ) " 의 형태로써 현재 실행중인 객체의 생성자로도 사용할 수 있습니다. 단, 반드시 생성자의 첫 문장으로만 사용이 가능합니다.
오버로딩
메소드 오버로딩(Method Overloading)이란, 하나의 클래스 안에 똑같은 이름으로 여러 개의 메소드를 정의하는 것을 말합니다.
예를 들어, 어떤 클래스의 생성자를 여러 개 만드는 경우가 오버로딩에 해당할 수 있습니다. 자바에서 클래스의 생성자는 직접 명시적으로 써주지 않아도 자동으로 하나의 생성자가 존재하게 됩니다. 그런데 그 디폴트 생성자 이외에 추가적으로 파라메터를 다르게 던져주어서 생성하려고 할때 오버로딩을 하게 되는 것입니다.
오버로딩할 때는 그 메소드명들 간에 파라메터의 개수가 다르거나, 개수가 같으면 파라메터의 타입이 다르거나 해야 합니다. 같은 메소드명으로 파라메터 개수도 같고 타입도 같아버리면 코드 에러 입니다.
객체지향의 특징 4가지 中 캡슐화(Encapsulation)
위의 코드는 캡슐화가 되어있지 않은 예시 코드였습니다. 캡슐화란 무엇일까요?
클래스 설계 시에 데이터와 기능을 클래스라는 틀에 넣어서 설계하고, 중요한 데이터나 복잡한 구현을 숨기고, 사용에 꼭 필요한 기능만을 공개하여 정의하는 기법을 캡슐화라고 합니다.
중요하거나 상세한 구현은 private 라는 접근 제한자를 사용해서 숨기고, 모두 사용하는 기능은 public 하게 접근 제한자를 둡니다. 클래스의 attribute는 private하게 숨기고 method 는 public 하게 두는 것도 이러한 캡슐화 구현의 방식입니다.
캡슐화는 정말 간단히 말하면 getter 와 setter 를 사용하는 것입니다. 아까의 그 Member 클래스에 세터와 게터를 추가해준다면 아래와 같이 만들어 볼 수 있겠습니다.
public void setName(String name) {
// 파라메터로 들어온 name 데이터 체크 로직
// 필요할 경우 데이터 조작 로직 등
this.name = name;
}
public void setId(int id) {
id += 100; //이런식
this.id = id;
}
이처럼 클래스 밖에서 직접 데이터를 조작하지 않고 getter 와 setter 를 통해서만 해당 데이터를 조작할 수 있도록 하는 것입니다. 마치 캡슐에 넣는 것 처럼 감싸서 은폐하는 것이지요. 이렇게 했을 때 좋은 점은, 체크로직이나 조작로직을 넣을 수 있으므로 데이터에 변경이 발생할 때나 유지보수할때 오류발생이 적어집니다. 즉 정보 은닉이라 합니다.
또 다른 장점은 인터페이스가 간결해진다는 것입니다. 예를 들어 이 Member 클래스가 Study 클래스와 메시지를 주고받는 상황이라고 가정할 때, Study 클래스는 Member 클래스의 세부 내용을 알 필요가 없이 게터와 세터를 사용하여서 값을 전달해주면 됩니다. 만약 Member 클래스에 변화가 있다고 해도 Member 클래스 내부로직만 수정하면 되고 Study 클래스에서는 변화사항을 알 필요가 없이 동일한 방법대로 데이터를 전달해주면 됩니다. 즉 다른 객체에 영향이 줄어들기 때문에 독립성이 유지가 되고 객체 간의 결합도도 낮아집니다.
요약하자면 필요한 기능만 공개되어 있으므로 사용이 편리해지고 코드의 유지보수가 용이해집니다. 대부분의 API 가 이러한 캡슐화가 적용되어 있습니다. java.lang이나 java.util은 표준 API의 패키지의 예시입니다.
TMI를 하나 더 얹어보자면, java.lang 패키지는 자바의 기본적인 클래스가 들어있는 패키지입니다. 개발자가 import 하지 않아도 사용할 수 있습니다. import가 안되어 있다면 컴파일러가 자동으로 처리해주기 때문입니다. java.lang API 안에는 Object 클래스, String 클래스, System 클래스 등이 들어 있습니다.
'IT,SW,Data,Cloud,코딩' 카테고리의 다른 글
비모수 검정: Mann-Whitney U Test 외 (0) | 2021.10.01 |
---|---|
빅데이터분석기사 필기 시험준비 - 나만의 암기포인트 (0) | 2021.09.30 |
머신러닝 초보자를 위한 강의와 책 추천 (0) | 2021.08.26 |
개발자이직 알아볼 때 점핏(jumpit) 플랫폼 사용하면 편리해요! (0) | 2021.07.27 |
Brightics 사용법 /초보자용 꿀팁 (0) | 2021.05.19 |
Logistic Regression, kNN(k-Nearest Neighbor), Naive Bayes (0) | 2021.04.21 |
상관분석이란? 상관계수 결과 해석 (0) | 2021.04.19 |
평균 비교 검정/ Bartlett's Test - One Way ANOVA - Tukey's Range Test (0) | 2021.04.19 |
댓글