KDT 공부 일지 - 2026.01.15

김시코·2026년 1월 15일

KDT 공부 일지

목록 보기
9/27

Interface (인터페이스) - 어떤 상호작용을하기 위한 무언가.

[개념적 인터페이스]
- 사람이 자동차를 사용하게 하기 위해서 자동차는 인터페이스를 제공해야 한다.
  └ 인터페이스: 핸들, 엑셀, 브레이크... 등
- 사람이 TV를 조작 하기 위해서는 리모컨이라는 인터페이스가 필요하고, 그 리모컨을 사람이 사용하기 위해서는 버튼이라는 인터페이스가 필요하다. 또한, 리모컨과 TV가 상호작용을 하기 위한 적외선 신호를 전달하는 2진수 코드 또한 인터페이스라고 할 수 있다. 페트병 객체와 뚜껑 객체는 서로 상호작용 하지만, 뚜껑은 물티슈와 상호작용 하지 않는다. 페트병과 뚜껑이 뚜껑이 닫힌다는 상호작용을 하기 위해서는 페트병 병목의 나사선과 뚜껑 안쪽의 나사선이 필요한데, 이것이 인터페이스다. 예시를 더 들어보자면 PC와 입출력 장치(키보드, 마우스, 모니터...)를 연결하기 위한 인터페이스로는 USB포트가 있다. 소프트웨어 관점으로 보자면 사용자가 네이버 프로그램을 사용할때의 인터페이스는 키자마자 보이는 화면이 된다. 여기서 입출력이 다 이루어지기 때문. 이 화면이 흔히 말하는 UI다. (UI는 User Interface를 줄여서 부르는 것)

화면의 구성 자체를 레이아웃이라고 하며, UI가 좋은 프로그램은 레이아웃이 좋은 것이고, 좋은 UI는 UX를 잘 따르는 것이라고 한다. 여기서 UX는 User eXperience의 줄임말이다. UX는 사용자가 이 버튼을 눌렀을때 어떤 일이 벌어질 것인지 예상이 가능하게 해야 하는 것. 예를들면, 사람은 빨간색을 위험한 색으로 인지하고 있기 때문에 삭제를 할때는 빨간색을 사용해야 하고, 초록색을 사용하면 UX를 따르지 않는 것이다. 또 하나의 예시는 사람들은 보통 ... 을 누르면 상세 메뉴가 나올 것이라고 예상하기 때문에 그것을 따라서 ...를 누르면 상세메뉴가 나와야 하며, 로그인 페이지가 나오면 안된다. 그렇게 되면 사용자가 어떠한 버튼을 눌렀을때의 결과를 의심하기 시작하기 때문에 그렇게 만들어서는 안되며, 어떠한 기능의 아이콘이 어떻게 생겨야 사용자가 기능을 명확하게 파악하고 사용할 수 있는지, 어떠한 기능이 어디에 위치해야 사용자들이 불편함을 겪지 않고 사용 할 수 있는 지 등을 잘 파악하고 다양한 경우의 수를 따져가면서 배치를 하는것이 UX이다. 이렇듯, UX는 사용자의 심리를 잘 파악하는것이 중요하기 때문에 심리학이 중요하고 할 수 있다.

[UI의 종류]
GUI: Grapics User Interface - 그래픽 기반으로 이루어지는 인터페이스 (키보드, 마우스 등...)
NUI: Natural User Interface - 음성인식 목소리 기반으로 이루어지는 인터페이스
CLI: Command Line Interface - 마우스 클릭 없이 키보드의 커맨드입력 기반으로 이루어지는 인터페이스

[JAVA에서의 인터페이스]
인터페이스의 결과적인 목표는 인터페이스의 표준을 정의해서 모듈을 손쉽게 교체 할 수 있게 하기 위해 사용하기도 한다.
지금 말하는 모듈들은 컴포넌트를 말하는것이며, Interface는 public으로 정의된 메서드들이 Interface가 되는 것. StudentManagerV2 의 경우에는 registery메서드, list 메서드 등이 인터페이스가 된다.

[P1]
package p1;

public class App {
    public static void main(String[] args) {
        // 코드 내의 개념적 인터페이스
        //  ㄴ public으로 선언된 모든 것 - 특히 public 메서드
        // 자바 문법의 인터페이스
    }
}

// 자바 문법 interface: 표준(규약, protocol)을 정의하는 문법.
interface AAA {     // AAA 클래스를 상속받는 모든것은 이 표준을 따르는 것
    // ...
}

사실 이것들은 이론이기 때문에 실제 실무에서는 시간적인 제약이 있기 떄문에 잘 사용하지 않는다. 실무는 시간과 생산성을 중점으로 돌아가기 때문에.


Ioc는 제어의 역행을 나타내는 개념적 요소이고, 그걸 작성하기 위한 것이 DI. 이 두가지 이론을 구현하는 구현체가 Container이다. DI로 구현 방법을 알수 없게 하여, 철저하게 역할을 분리 하기 위해 사용한다. 구현을 알아야 한다면 메서드의 로직부터 알아봐야 하기 때문.
DI(Dependency Injection, 의존성 주입): 객체가 필요한 다른 객체를 직접 만들지 않고, 외부에서 넣어주는 것
Ioc(Inversion of Control): 객체 생성과 흐름 제어의 주도권이 개발자가 아니라 프레임워크에 있는 것
Container: 자바 빈(bean)을 제어하는 것이다. 정확히는 컨테이너에 의해 생성되고 관리되는 인스턴스를 지칭하는 용어.
bean: 인스턴스를 지칭하는 별칭 - 우리가 아는 그 콩 맞다.
- 스프링에서 가장 중요한 것이 컨테이너.

[DI, IoC, Container 적용 예시]

[App]
package polymorphism;
// 다형성 (다형성이라고만 해도상속 다형성 오버라이딩이 다 포함)
// DI(의존 주입, 이론), Ioc(제어의 역행, 이론), Container(인스턴스 컨테이너)
// DI(코드기술)를 통해 Ioc(대표하는 개념)가 성립되고 컨테이너(코드기술)에서 마무리 한다
public class App {
    public static void main(String[] args) {
    /*  Tv tv = new SamsungTv();

        User user = new User(tv);   // 괄호안에 tv 넣는게 DI. 그 전에 DI가 가능하게 setter 혹은 생성자를 만들어 놔야 함.
        user.watchTv(); */

        Container container = new Container();
        User user = container.getUser();
        user.watchTv();    
    }
}
[Tv]
package polymorphism;
// 유지버수 비용을 줄이기 위해 표준을 정의 함.
public interface Tv {
    public void powerOn();
    public void powerOff();
    public void soundUp();
    public void soundDown();
}
[Speaker]
package polymorphism;

public interface Speaker {
    public void volumeUp();
    public void volumeDown();
}
[User]
package polymorphism;

public class User {
    // 이 클래스에서 다른 클래스가 사용되면 의존하는 것.

    // final를 붙이는 이유는 주입이 한번만 이루어져서 안전해지기 때문. 필수는 아님
    private final Tv tv;  // 가장 보편적인 DI 사용법 = new LgTv; 하면 컴포지션. 의존 해버리는 것.
    // 컨스트럭터(생성자) 주입, setter 주입. 원래는 하나만 사용해야 한다.

    // 생성자 정의 (DI가 가능하게 함) - 인스턴스를 생성할때 주입이 됨.
    public User(Tv tv) {        // 컨스트럭터 주입
        this.tv = tv;
    }

    /*
    // setter (DI 가능하게 함) - 중간에 LG Tv에서 samsumg Tv로 바뀌어야 할때 유리
    public void setTv(Tv tv) {  // setter 주입
        this.tv = tv;
    }   */

    public void watchTv() { // 여기 괄호 안에 Tv tv 쓰는것도 가능은 함
    //  Tv tv = new LgTv(); // 여기만 바꿔주면 LGTV와 SamsungTV가 쉽개 바뀐다. (DI, Ioc 적용 전)
        System.out.println("사용자가 TV 사용 시작");

        // Tv를 의존하는 것.
        tv.powerOn();
        tv.soundDown();
        tv.soundDown();
        tv.soundDown();
        tv.soundDown();
        tv.soundUp();
        tv.powerOff();
        // 여기서 Samsung Tv를 Lg Tv로 바꾸기 위해서는 어마무시 한 수정을 해야 함. (코드를 다 들어내야 함)

        System.out.println("사용자가 TV 사용 종료");
    }
}
[SamsungTv]
package polymorphism;

public class SamsungTv implements Tv {

    private Speaker speaker;

    public SamsungTv(Speaker speaker) {
        this.speaker = speaker;
    }

    public void powerOn() {
        System.out.println("[삼성]  전원이 켜진다.");
    }

    public void powerOff() {
        System.out.println("[삼성] 전원이 꺼진다.");
    }

    public void soundUp() {
        speaker.volumeUp();
    }

    public void soundDown() {
        speaker.volumeDown();
    }
}
[LgTv]
package polymorphism;

public class LgTv implements Tv {

    private Speaker speaker;

    public LgTv(Speaker speaker) {
        this.speaker = speaker;
    }

    public void powerOn() {
        System.out.println("[LG] 전원이 켜진다");
    }

    public void powerOff() {
        System.out.println("[LG] 전원이 꺼진다");
    }
    
    public void soundUp() {
        speaker.volumeUp();
    }

    public void soundDown() {
        speaker.volumeDown();
    }
}
[container]
package polymorphism;

public class Container {
    // 나중에는 Factory는 빈을 생성하는 공장.
    // 컨테이너는 생성된 빈을 가두는 곳으로 나눠서 작동한다.
    private User user;
    public Container() {
        // 인스턴스 생성, 조립
        Speaker appleSpeaker = new AppleSpeaker();
        Speaker sonySpeaker = new SonySpeaker();

        Tv samsungTv = new SamsungTv(appleSpeaker);
        Tv lgTv = new LgTv(appleSpeaker);

        user = new User(samsungTv);
    }

    public User getUser() {
        return user;
    }
}
[AppleSpeaker]
package polymorphism;

public class AppleSpeaker implements Speaker{
    public void volumeUp() {
        System.out.println("[애플] 소리가 커집니다.");
    }

    public void volumeDown() {
        System.out.println("[애플] 소리가 작아집니다.");
    }
}
[SonySpeaker]
package polymorphism;

public class SonySpeaker implements Speaker {
    public void volumeUp() {
        System.out.println("[소니] 소리가 커집니다.");
    }

    public void volumeDown() {
        System.out.println("[소니] 소리가 작아집니다.");
    }
}

좋은 코드란? "유지보수"가 잘 될 수 있는 코드.

  • 가독성이 좋아야 함. (로직을 보지 않고도 코드를 이해 할 수 있어야 함)
  • 결합도가 낮은 코드

    사실 좋은 코드를 만들기 위해서는 "생산성"이 저하되며, 돈을 버는 모든 일은 시간 제한이 존재한다.
    유지보수 비용이 생산 비용보다 클 겅우에는 좋은 코드를 생산해야 하지만, 생산 비용이 유지보수 비용보다 큰 경우에는 좋은 코드를 일부 포기 해야한다.

    무한히 시간이 존재한다는 가정 하에, 좋은 코드를 만든다면?
    당연히 연습도 많이 해야 하지만, 좋은 코드 및 각종 디자인 패턴과 관련된 책을 읽고 공부를 하고, SOLID 원칙을 따르면 된다.

    [SOLID 원칙]
    - S: 단일 책임 원칙 (SRP)
    - O: 개방 폐쇠 원직 (OCP)
    - L: 리스코프 치환 원직 (LSP)
    - I: 인터페이스 분리 원칙 (ISP)
    - D: 의존 역전 원칙 (DIP)

- 하지만 생산성과 유지보수성을 둘 다 높일 수 있는 방법이 있는데, 그것이 프레임워크를 사용하는 방법이다.

*프레임워크란? 설계를 이미 완성해둔 것을 말한다.

[SOLID 원칙]

[P2 - ISP]
package p2;

public class App {
    public static void main(String[] args) {
        
    }
}
// 인터페이스 분리 원칙: ISP

interface Animal {
    void move();
    void eat();
    void sleep();
    //...
}   // 너무 거대한 인터페이스를 만들지는 마라.

interface Moveable {
    void move();
}
interface Eatable {
    void eat();
} // 이런식으로 인터페이스를 잘게 쪼개라

// class Dog implements Moveable, Eatable {
//     // ...
// }
[P3 - OCP]
package p3;

import java.util.Map;
import java.util.HashMap;

public class App {
    public static void main(String[] args) {
        // OCP: 개발 폐쇠 원직
        // 기능의 확장에는 열려 있어야 하지만, 코드 수정은 닫혀 있어야 한다.
    }
}

class Controller {

    private ServiceContainer serviceContainer = new ServiceContainer();

    public void run() {
        String command = "3";

        Service service = serviceContainer.get(command);
        service.execute();
    }
}

// 이러는 이유는 if문을 최대한 쓰지 않고 조립식으로 만들기 위해서.
interface Service {
    public void execute();
}

// 여기서부터는 메서드명이 아닌, 클래스 명이 중요해진다. + 앞으로는 깊게 들어가면 다 다형성이다.
class RegisterStudentService implements Service {
    public void execute() {
        //.. 등록 구현
        System.out.println("등록 기능 실행");
    }
}

class ListStudentService implements Service {
    public void execute() {
        //.. 목록 구현
        System.out.println("목록 기능 실행");
    }
}

class SerchStudentService implements Service {
    public  void execute() {
        //.. 검색 구현
        System.out.println("검색 기능 실행");
    }
}

class Number7Service implements Service {
    public void execute() {
        System.out.println("7번 기능 실행");
    }
}

class ServiceContainer {
    private Map<String, Service> map = new HashMap<>();

    public ServiceContainer() {
        map.put("1", new RegisterStudentService());
        map.put("2", new ListStudentService());
        map.put("3", new SerchStudentService());
        map.put("3", new Number7Service());
    }

    public Service get(String command) {
        return map.get(command);
    }
}

[@Override]

[P4]
package p4;

public class App {
    public static void main(String[] args) {
        
    }
}

interface AAA {
    public void test1();
}

class AAAImp1 implements AAA {
    @Override
    public void test1() {

    }

    // 여기에 @Override 쓰면 오류. 오버라이드 하고 있지 않기 때문에
    public void test2() {

    }
}
profile
취업하고싶다...

0개의 댓글