Semantic Kernel이란?Semantic Kernel은 기존 애플리케이션에 AI 기능을 쉽게 통합할 수 있게 해주는 오픈소스 SDK. 특히 LLM(대규모 언어 모델)과 기존 코드를 연결해주는 다리 역할을 한다.쉽게 말해, 개발자가 ChatGPT 같은 언어 모델의 파워를 자신의 앱에 손쉽게 가져올 수 있는 도구 모음.구성 요소1. 커널(Kernel)Semantic Kernel의 핵심 컴포넌트로, AI 모델과 스킬을 오케스트레이션하는 중앙 엔진 역할을 한다. 커널은 다양한 AI 서비스에 대한 추상화 계층을 제공하여 개발자가 특정 AI 제공업체에 종속되지 않도록 한다. 2. 스킬(Skills)스킬은 특정 작업을 수행하기 위한 기능 모음이다. 크게 두 가지 유형이 있다:시맨틱 스킬(Semantic Ski..
전체 글
개발 기록아이템 14 : Comparable을 구현할지 고려하라Comparable 인터페이스는 객체 간에 자연스러운 순서를 정의하는 방법을 제공하며, 클래스의 인스턴스들이 정렬 가능함을 의미한다. 이 인터페이스를 구현하면 compareTo 메서드를 통해 객체 간의 순서를 비교할 수 있게 된다. 순서가 명확한 VO(Value Object) 클래스를 작성할 때는 Comparable을 구현하는 것이 권장된다.1. compareTo의 규약compareTo 메서드는 객체의 순서를 비교하고 다음 값을 반환한다:음의 정수: 이 객체가 주어진 객체보다 작을 때0: 두 객체가 같을 때양의 정수: 이 객체가 주어진 객체보다 클 때compareTo는 다음의 규칙을 따라야 한다두 객체의 순서를 바꿔서 비교해도 예상된 결과가 나와야 한..
아이템 13 clone 재정의는 주의해서 진행하라Cloneable 인터페이스는 객체 복제를 허용하는 클래스임을 표시하는 용도다. 하지만 clone 메서드는 Cloneable이 아닌 Object 클래스에 정의되어 있으며, 기본적으로 protected로 선언되어 있어 상속을 통해 접근해야 한다. Cloneable 인터페이스에는 실제로 메서드가 존재하지 않지만, 이 인터페이스를 구현하는 클래스에서 clone을 호출할 수 있게 된다.1. clone의 명세는 허술하다clone의 명세는 매우 허술하며, 이를 제대로 구현하는 책임은 개발자에게 전가된다. 예를 들어, 상위 클래스에서 super.clone() 대신 생성자를 호출해 반환해도 컴파일러는 이를 문제없이 통과시킨다. 이는 clone 메서드의 동작이 일관되지 않..
아이템 12 toString을 항상 재정의하라toString 메서드는 객체의 정보를 사람이 읽기 쉽게 반환해야 한다. 잘 재정의된 toString 메서드는 객체의 주요 정보를 쉽게 확인할 수 있게 해주며, 특히 디버깅에 매우 유용하다. 따라서, 모든 하위 클래스에서 toString을 재정의하는 것이 권장된다.좋은 toString 작성 방법주요 정보를 포함하라: toString 메서드는 그 객체가 가진 핵심 정보를 포함하는 문자열을 반환해야 한다. 이는 그 객체를 설명할 수 있는 충분한 정보가 들어있어야 한다.명확한 의도: 반환할 문자열의 포맷을 주석으로 명시하거나 문서화해서 다른 개발자들이 그 형식을 이해하고 사용할 수 있게 해야 한다.대체 API 제공: toString이 단순히 디버깅용이어야지, 이를 ..
아이템 11 equals를 재정의하려거든 hashCode도 재정의하라equals 메서드를 재정의했다면 반드시 hashCode도 재정의해야 한다. 그렇지 않으면, 두 객체가 논리적으로 같더라도 다른 해시값을 반환하여 해시 기반 컬렉션(HashMap, HashSet)에서 일관성이 깨지거나 제대로 작동하지 않을 수 있다.1. hashCode와 equals의 관계일반 규약에 따르면, 두 객체가 equals에 의해 같다고 판별되면, 반드시 같은 hashCode를 반환해야 한다.그렇지 않으면, 같은 논리적 객체가 서로 다른 해시 버킷에 들어가고, 해시 기반 자료구조가 올바르게 동작하지 않게 된다.2. hashCode 규약객체의 동일성을 나타내는 필드를 기준으로 일관된 해시코드를 반환해야 한다.다음의 규칙을 따라야 ..
아이템 10 - equals는 일반 규약을 지켜 재정의하라다음 상황중 하나에 해당한다면 equals를 굳이 재정의하지 않는 것이 최선1.각 인스턴스가 본질적으로 고유하다.객체가 고유한 개체라면, 모든 객체는 자신과만 같고, 다른 객체와는 절대 같을 수 없다. 예를 들어, Thread, Socket 같은 시스템 자원 핸들러나 고유한 상태를 가진 객체들은 인스턴스마다 서로 다른 개체로 취급되므로 equals를 재정의할 필요가 없다. 이런 객체는 동등성을 비교하는 게 무의미하기 때문에 equals 메서드를 굳이 재정의하지 않아도 된다.2. 인스턴스의 논리적 동치성을 검사할 일이 없다.논리적 동등성이란 두 객체가 값으로 비교될 때 같다고 판단되는 경우를 말한다. 하지만 이 규칙에 해당하는 경우는 논리적 동등성을..
try-finally 방식static String firstLineOfFile(String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { br.close(); }}try-finally는 자원을 사용한 후 반드시 반환하기 위해 finally 블록을 사용한다. 위 코드에서는 파일을 읽기 위해 BufferedReader 객체를 생성하고, 파일 읽기 작업이 끝난 후 finally 블록에서 자원을 반환(close)하고 있다.문제점예외 발생 시 자원 누수 가능성: 파일을 읽는 도중 예외가..
아이템 8 : finalizer와 cleaner 사용을 피하라자바에서 제공하는 객체 소멸자인 finalizer와 cleaner는 여러 문제로 인해 사용을 피하는 것이 좋다. 이들은 예측 불가능한 실행 타이밍, 성능 문제, 보안 취약점 등을 내포하고 있다.1. 예측할 수 없는 실행 타이밍finalizer와 cleaner는 객체가 더 이상 필요 없어졌을 때, 즉 가비지 컬렉터가 객체를 회수할 때 호출된다.수행 시점이 불확실하며, 제때 실행되지 않기 때문에 즉시 수행되어야 할 작업에는 적합하지 않다. 예를 들어, 파일 닫기나 데이터베이스 연결 해제 같은 작업에 사용할 수 없다.2. 실행 여부 보장 안 됨finalizer와 cleaner는 실행 여부조차 보장되지 않는다. 프로그램이 갑작스럽게 종료되면 일부 객체..
아이템 7 - 다 쓴 객체 참조를 해제하라자바처럼 가비지 컬렉터를 갖춘 언어를 쓰게되면 자칫 메모리 관리에 더 이상 신경 쓰지 않아도 된다고 오해할 수 있는데, 절대 사실이 아니다.public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] ..
아이템 6 - 불필요한 객체 생성을 피하라불필요한 객체 생성을 피하는 것은 메모리 효율성과 성능을 향상시키는 데 중요한 포인트다.String 객체 비교 예시String 리터럴 비교String str1 = "abc";String str2 = "abc";System.out.println(str1 == str2); // trueSystem.out.println(str1.equals(str2)); // true두 문자열은 String Constant Pool에 저장되며, 같은 리터럴이기 때문에 동일한 객체를 참조한다. 따라서 ==로 비교해도 true가 나오고, equals 메서드로 비교해도 true다.new 키워드로 생성한 StringString str1 = "abc";String str2 = ne..
아이템 5 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다. 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식인 의존 객체 주입 방식을사용해야한다.예를 들어 unitPrice * usage를 통한 최종 금액에서, 할인율 20%(소수점 내림)를 적용하려고 한다. 아래처럼 코드를 만들 수 있다.@Overridepublic long calculateEachCharge(CityGasUser user) { long unitPrice = user.getUnitPrice(); long usage = user.getUsage(); return unitPrice * usage * 80 / 1..
아이템 4 인스턴스화를 막으려거든 private 생성자를 사용하라인스턴스화를 막으려면 private 생성자를 사용하여 클래스 외부에서 객체를 생성하지 못하도록 제한할 수 있다. 이를 통해 클래스의 유일한 인스턴스 또는 정적 메서드를 통해서만 객체에 접근하도록 유도할 수 있다.싱글턴 패턴public class DatabaseConnection { // 유일한 인스턴스를 저장할 정적 변수 private static final DatabaseConnection INSTANCE = new DatabaseConnection(); // private 생성자: 외부에서 인스턴스를 생성할 수 없도록 함 private DatabaseConnection() { // 초기화 코드 }..
아이템 3 private 생성자나 열거 타입으로 싱글턴임을 보증하라싱글턴(Singleton) = 인스턴스를 오직 하나만 생성할 수 있는 클래스필드로 선언된 정적 인스턴스public class Elvis { public static final Elvis INSTANCE = new Elvis(); // 정적 필드로 유일한 인스턴스를 선언 private Elvis() { } // 생성자는 private로 설정 public void leaveTheBuilding() { System.out.println("Whoa baby, I'm outta here!"); } public static void main(String[] args) { Elvis elvis = E..
아이템 2 생성자에 매개변수가 많다면 빌더를 고려하라정적 팩터리와 생성자에는 선택적 매개변수가 많을 때 적절히 대응하기 어렵다는 공통제약이 있다.보통 클래스용 생성자 혹은 정적 팩터리는 점층적 생성자 패턴, 자바빈즈 패턴 등을 사용하였다.점층적 생성자 패턴 (Telescoping Constructor Pattern)점층적 생성자 패턴은 여러 개의 생성자 오버로드를 사용하여 다양한 초기화 방법을 제공하는 패턴이다.public class NutritionFacts { private final int servingSize; // 필수 private final int servings; // 필수 private final int calories; // 선택 private fi..
아이템 1 생성자 대신 정적 팩터리 메서드를 고려하라정적 팩터리 메서드 = 객체의 생성을 담당하는 클래스 메서드public class Car { private String model; // private 생성자 private Car(String model) { this.model = model; } // 정적 팩터리 메서드 public static Car create(String model) { return new Car(model); } public String getModel() { return model; }}public class Main { public static void main(..