본문 바로가기
CS/자바

[JAVA] 프레임워크

by Hoozy 2023. 3. 10.

이전 게시글 JAVA 심화 2

https://hoozy.tistory.com/entry/JAVA-%EC%8B%AC%ED%99%94-2

 

[JAVA] 심화 2

이전 게시글 JAVA 심화 1 https://hoozy.tistory.com/entry/JAVA-%EC%8B%AC%ED%99%94-1 카테고리 : 자바(심화) 파일 1. java.io.File File 클래스의 createNewFile() 메소드를 이용하여, 새로운 파일 및 디렉토리를 생성할 수

hoozy.tistory.com

카테고리 : 자바(프레임워크)

웹 애플리케이션을 만들고 유지하는데 사용되는 강력한 오픈소스 JAVA 프레임워크는 2가지가 있습니다.

1. Spring
  • 개발할 때 초기 설정이 오래걸리고 어렵다.

엔터프라이즈용 JAVA 애플리케이션 개발을 편하게 할 수 있게 해주는 오픈소스 경량급 애플리케이션 프레임워크

  • 개발 초기에 기본적인 설정과 적용시킬 기술들만 잘 선택해준다면, 기술보다는 애플리케이션의 로직 자체에 더 집중하여 비즈니스 로직을 구현할 수 있습니다.
  • 오픈소스라서 어떤 개인 및 기업도 스프링을 사용하여 웹 애플리케이션을 개발 할 수 있으며, 필요하다면 스프링의 코드를 일부 수정해도 무관합니다.

경량급

  • 스프링은 수십 개의 세부 모듈 및 수십 만줄의 방대한 코드로 이루어진 프레임워크입니다.
  • 기존에 스프링 대신 사용하던 기술들과 비교하여, 스프링을 사용했을 때에 개발자가 작성해야 할 코드가 상대적으로 단순함을 표현하기 위해서 경량급 이라고 한다.

애플리케이션 프레임워크

  • 프레임워크 : 어떠한 목적을 쉽게 달성할 수 있도록 해당 목적과 관련된 코드의 뼈대를 미리 만들어둔 것.
    • 애플리케이션 프레임워크 : 애플리케이션을 개발하는 데에 있어 필요한 모든 업무 분야 및 모든 기술과 관련된 코드들의 뼈대를 제공합니다.

특징

1. POJO 프로그래밍 지향

  • 스프링의 가장 큰 특징은 POJO 프로그래밍을 지원하는 것이다.
  • POJO (Plain Old JAVA Object) : 순수 JAVA만을 통해서 생성한 객체
  • 순수 JAVA만을 사용한다는 것은 JAVA 및 JAVA의 스펙에 정의된 기술만 사용한다는 의미이다.
// 필드와 Getter, Setter만 존재하는 기본적인 POJO 예시
public class Person {
    private String name;

    public String getName() { // Getter
        return name;
    }

    public void setName(String name) { // Setter
        this.name = name;
    }
}

중요한 이유

  • 만약 외부 라이브러리를 import하여 라이브러리의 메소드를 사용하고 있는 객체가 있다고 하면, 이 객체는 순수 JAVA 외에 외부 기술을 사용하고 있으므로, POJO가 아닙니다.
  • 이 때, 이 객체가 사용하고 있는 기술이 사라지거나, 새로운 기술이 등장하여 기존 기술과 관련된 코드를 모두 고쳐야 하는 상황이 발생하면, 해당 기술을 사용하고 있는 모든 객체들의 코드를 전부 바꿔야합니다. 이는 해당 객체가 외부 모듈에 직접적으로 의존하고 있기 때문에 발생하는 일이다.
  • 반면, POJO는 순수 JAVA만 사용하여 만든 객체이므로 특정 기술이나 환경에 종속되지 않습니다.
  • 따라서, 외부 기술이나 규약의 변화에 얽매이지 않아서 보다 유연하게 변화와 확장에 대처할 수 있다.
  • 이러한 POJO를 사용하여 비즈니스 로직을 구현하면 객체지향 설계를 제한없이 적용할 수 있으며, 코드가 단순해져 테스트와 디버깅 또한 쉬워진다.
  • 이처럼 비즈니스 로직을 구현하는데 POJO를 적극적으로 활용하는 프로그래밍 패러다임을 POJO 프로그래밍 이라고 합니다.
  • POJO 프로그래밍을 위해 스프링이 지원하는 기술인 IoC/DI, AOP, PSA에 대해서 알아보겠습니다.

2. IoC / DI

  • IoC (Inversion of Control) : 제어의 역전.
  • DI (Dependency Injection) : 의존성 주입.
  • JAVA는 객체지향 언어이므로, 객체들 간의 관계를 적절히 맺어주는게 중요한 요소입니다.
  • A 인스턴스가 B 인스턴스의 메소드를 호출하고 있다면 A는 B와 의존 관계를 맺은 것이며, 이 둘의 관계를 "A가 B에 의존하는 관계"라고 표현할 수 있습니다. A가 B의 기능을 가져다 사용하는 것이기 때문에.
  • 만약, A 인스턴스가 B 인스턴스 말고 C 인스턴스를 사용하고 싶으면, C 인스턴스를 새로 생성해서 A 인스턴스에서 C 인스턴스를 호출해서 사용하면 된다.
  • 하지만, 기존에 B를 사용하던 객체가 A 뿐만 아니라, 수십개 이상이 있다면 모든 객체의 코드를 수정해주어야 합니다.
  • 이때 스프링을 사용하면 위처럼 변화가 발생한 상황에 최소한의 수정만으로 변화를 유연하게 수용할 수 있습니다.
  • 스프링을 사용하여 좀 더 변화에 유연하게 대응할 수 있는 코드를 작성하려면 아래와 같이 코드를 수정해주어야 합니다.
// 1. A가 사용하는 메소드를 인터페이스 I의 추상 메소드를 정의한 다음 (추상화)
interface I {
    void example();
}

class A {

    // 3. 그 다음, 인터페이스 타입의 필드를 선언,
    private I i;

    // 4. 생성자를 통해 외부로부터 인스턴스를 받아와 i를 초기화해준다. (다형성)
    public A(I i) {
        this.i = i;
    }

    public void methodA() {
        // 5. 외부로부터 받아온 인스턴스의 메소드를 호출합니다.
        i.example();
    }
}

// 2. A가 사용하는 메소드를 가진 객체들이 I를 구현하도록 합니다.
class B implements I {
    public void example() {
        ...
    }
}

class C implements I {
    public void example() {
        ...
    }
}
  • 위에서 A는 자신이 사용할 객체를 스스로 생성하지 않고, 생성자를 통해 외부로부터 받아오고 있습니다.
  • A는 자신이 사용할 객체를 알지 못하며, 그저 i에 할당된 인스턴스에 example()이라는 메소드가 존재한다는 것만 알고 있다.
  • 이때 스프링이 A가 사용할 객체를 결정하고 생성해서 A가 인스턴스화될 때 인자로 전달해줍니다. 그래야 A는 B의 것이든, C의 것이든 example() 메소드를 호출할 수 있을 것입니다.
  • 이처럼 스프링을 사용하면 애플리케이션의 로직 외부에서 A가 사용할 객체를 별도로 설정 가능합니다.
  • 개발자가 설정 클래스 파일에 A가 사용할 객체를 C로 설정해두면, 애플리케이션이 동작하면서 스프링이 설정 클래스 파일을 해석하고, 개발자가 설정해둔대로 C 객체를 생성하여 A의 생성자의 인자로 C를 전달해줍니다.
  • 이처럼 개발자가 아닌 스프링이 A가 사용할 객체를 생성하여 의존 관계를 맺어주는 것을 IoC 라고 하며, 그 과정에서 C를 A의 생성자를 통해 주입해주는 것을 DI 라고 합니다.

3. AOP (Aspect Oriented Programming) : 관심 지향 프로그래밍

  • 애플리케이션을 개발할 때에 구현해야 하는 기능들은 크게 공통 관심 사항핵심 관심 사항으로 분류할 수 있습니다.
  • 핵심 관심 사항 : 애플리케이션의 핵심 기능과 관련된 관심 사항으로, 커피 주문 애플리케이션을 예로 든다면, 메뉴 등록하기, 주문하기, 주문 변경하기 등이 있습니다.
  • 공통 관심 사항 : 모든 핵심 관심 사항에 공통적으로 적용되는 관심 사항들으로, 위의 핵심 관심 사항에서의 로깅, 보안 등과 관련된 기능들이 공통적으로 적용되어야 하는 사항입니다.
  • 만약, 공통 관심 사항이 모든 핵심 관심 사항에 적용되어 있을 때, 공통 관심 사항을 수행하는 로직이 변경되면 모든 중복 코드를 찾아서 일일이 수정해주어야만 합니다.
  • 이처럼 공통 관심 사항 코드의 중복을 해결하기 위해서는 공통 관심 사항과 관련된 기능들을 별도의 객체로 분리한 다음, 분리해낸 객체의 메소드를 통해 공통 관심 사항을 구현한 코드를 실행시킬 수 있도록 해야합니다.
  • 위처럼 공통 기능을 비즈니스 로직으로부터 분리해내는 것을 AOP라고 합니다.

4. PSA (Portable Service Abstraction) : 일관된 서비스 추상화

  • 스프링을 사용하면 여러 DBMS(MySQL, Oracle, MariaDB 등)마다 동일한 사용방법을 유지한 채로 바꿀 수 있습니다.
  • 이는 스프링이 데이터베이스 서비스를 추상화한 인터페이스를 제공해주기 때문입니다.
  • 스프링은 JAVA를 사용하여 데이터베이스에 접근하는 방법을 규정한 인터페이스를 제공하고 있으며, 이를 JDBC(Java Database Connectivity) 라고 합니다.
  • 각 데이터베이스를 만든 회사들은 자신의 데이터베이스에 접근하는 드라이버를 JAVA 코드의 형태로 배포하는데, 이 드라이버에 해당하는 JAVA 코드의 클래스가 JDBC를 구현합니다. 따라서, JDBC를 기반으로 하여 데이터베이스 접근 코드를 작성해두면, 이후에 데이터베이스를 바꾸어도 기존에 작성한 데이터베이스 접근 로직을 그대로 사용할 수 있습니다.
  • JDBC처럼 특정 기술과 관련된 서비스를 추상화하여 일관된 방식으로 사용될 수 있도록 한 것을 PSA라고 합니다.
2. Spring Boot
  • Spring에 Bootstrap을 더한 것으로, Spring에 비해 장점이 많다.
    1. 초기 환경설정이 매우 간편하다. 매우매우 간편하다.
    2. 버전 관리가 매우 편하다.
    3. 내장 서버가 존재하여, 서버를 따로 설치, 관리를 할 필요가 없다.
      • 스프링에서는 배포를 할 때 별도의 외장 웹서버를 설치하고, 프로젝트를 War 파일로 빌드하여 배포를 진행하여야 하지만, 스프링부트는 자체적인 웹 서버를 내장하고 있어 빠르고 간편하게 배포를 진행할 수 있습니다.
      • 스프링부트는 독립적으로 실행 가능한 Jar 파일로 프로젝트를 빌드할 수 있어, 클라우드 서비스 및 도커와 같은 가상화 환경에 빠르게 배포할 수 있습니다.
Build Tools
  • 빠른 기간 동안에 계속해서 늘어나는 라이브러리의 추가
  • 프로젝트를 진행하며 라이브러리의 버전을 동기화하기 어려움을 해소하기 위해 등장
  • 아래 순서대로 최근에 나온 도구
  1. Gradle
  • Maven 대비 장점
    1. JAVA, C/C++, Python 등 여러 언어 지원
    2. 안드로이드 앱을 만드는데 필요한 안드로이드 스튜디오의 공식 빌드 시스템
    3. JAVA와 거의 비슷한 코드를 써서 빌드 처리 관리 가능
    4. 훨씬 적은 양의 스크립트로 짧고 간결하게 작성이 가능하다.
    5. Groovy라는 언어로 작성해야 해서 익숙하진 않지만, 확장성이 뛰어나다.
    6. 멀티 프로젝트에서 특정 설정을 상속 받지 않고, 설정 주입 방식으로 빌드 속도가 10~100배 가량 빠르다.
  • 빌드의 자동화에 대한 요구 증가에 따른 JVM 기반 빌드 도구
  • 기존의 Ant와 Maven의 장점을 합쳐 보완
  • JAVA 혹은 Groovy를 이용해 로직을 개발자에 의도에 따라 설계 가능
    • Groovy : JVM에서 실행되는 스크립트 언어
  • 오픈소스 기반의 build 자동화 시스템으로 Groovy 기반 DSL(Domain-Specific Language)로 작성
  • Build-by-Convention을 바탕으로함 -> 스크립트 규모가 작고 읽기 쉬움
  • Multi 프로젝트의 빌드를 지원하기 위해 설계됨
  • 설정 주입 방식 (Configuration Injection)
  • 초기 프로젝트 설정 시간 절약 가능
  • 기존의 Maven 이나 Ivy등과 같은 빌드 도구들과도 호환 가능
  • 예제

buile.gradle 파일

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.0.4'
    id 'io.spring.dependency-management' version '1.1.0'
}

group = 'com.hoozy'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    // 아래처럼 간편하게 빌드가 가능하다.
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // jpa
    implementation 'mysql:mysql-connector-java:8.0.29' // MySQL
    implementation 'org.projectlombok:lombok' // lombok
}

tasks.named('test') {
    useJUnitPlatform()
}
  1. Maven
  • 프로젝트에 필요한 모든 'Dependency(종속성)'을 리스트 형태로 Maven에게 알려 관리할 수 있도록 돕는 방식
  • Dependency를 관리하고, 표준화된 프로젝트를 제공
  • XML, remote repository를 가져 올 수 있음 -> 개발에 필요한 종속되는 'jar', 'class path'를 다운로드 할 필요 없이 선언만으로 사용 가능
  • 상속형 : 하위 XML이 필요 없는 속성도 모두 표기
  • 단점
    1. 라이브러리가 서로 종속할 경우 XML 복잡해짐
    2. 계층적인 데이터를 표현하기에 좋지만, 플로우나 조건부 상황을 표현하기엔 여러움
    3. 편리하나 맞춤화된 로직 실행이 어려움
  • 예제

pom.xml

<modelVersion>4.0.0</modelVersion>
    <groupId>com.bc</groupId>
    <artifactId>heal</artifactId>
    <name>Heal</name>
    <packaging>jar</packaging>
    <version>1.0.0-BUILD-SNAPSHOT</version>
    <properties>
        <java-version>1.8</java-version>
        <org.springframework-version>5.2.12.RELEASE</org.springframework-version>
        <org.springsecurity-version>5.3.9.RELEASE</org.springsecurity-version>
        <org.aspectj-version>1.9.6</org.aspectj-version>
        <org.slf4j-version>1.6.6</org.slf4j-version>
    </properties>
    <dependencies>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework-version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                 </exclusion>
            </exclusions>
        </dependency>
    </dependencies>    

3, Ant

  • JAVA 기반 빌드 도구
  • 개발자가 원하는 것을 개발할 수 있음
  • 각 프로젝트에 대한 XML 기반 빌드 스크립트 개발
  • 형식적 규칙이 없다 : 결과물을 넣을 위치를 정확히 알려줘야 하고, 프로젝트에 특화된 Target과 Dependency를 이용해 모델링
  • 절차적 : 명확한 빌드 절차 정의가 필요
  • 생명주기를 갖지 않기 때문에 각각의 target에 대한 의존관계와 일련의 작업을 정의해 주어야함
  • 단점
    1. 유연성이 높으나 프로젝트가 복잡해지면 각각 Build 과정 이해하기 어려워진다.
    2. 스크립트 재사용 어려움
    3. XML, Remote Repository를 가져올 수 없었음.
ORM (Object Relational Mapping)

1. JPA

  • JPA : JAVA ORM 기술에 대한 API 표준 명세
  • 특정 기능을 하는 라이브러리가 아니고, ORM을 사용하기 위한 인터페이스를 모아둔 것.
  • 자바 어플리케이션에서 관계형 데이터베이스를 어떻게 사용해야 하는지를 정의하는 방법 중 한 가지.
  • 단순한 명시이기 때문에 구현이 없다.

2. Hibernate

  • JPA 구현체 중 하나.
  • SQL을 사용하지 않고 직관적인 코드(메소드)를 사용해 데이터를 조작할 수 있습니다.
  • SQL을 직접 사용하지 않아도 JDBC API를 사용한다.
  • JPA와 Hibernate는 마치 자바의 인터페이스와 인터페이스를 구현한 클래스와 같은 관계이다.
  • JPA의 핵심인 EntityManagerFactory , EntityManager , EntityTransaction 을
    Hibernate에서는 각각 SessionFactory , Session , Transaction 으로 상속받고 각각 Impl로 구현합니다.
  • 장점
    1. 생산성
      • Hibernate는 SQL을 직접 사용하지 않고, 메소드 호출만으로 query가 수행된다.
      • 반복적인 SQL 작업과 CRUD 작업을 직접 하지 않으므로 생산성이 매우 높아진다.
    2. 유지보수
      • 테이블 컬럼이 변경되었을 경우, Mybatis에서는 관련 DAO의 파라미터, 결과, SQL 등을 모두 확인하여 수정해야 하지만, JPA는 JPA가 이런 일들을 대신 해주기 때문에 유지보수 면에서 좋다.
    3. 객체지향적 개발
      • 객체지향적으로 데이터를 관리할 수 있기 때문에 비즈니스 로직에 집중할 수 있다.
      • 로직을 쿼리에 집중하기 보다 객체 자체에 집중할 수 있다.
    4. 특정 벤더에 종속적이지 않다.
      • 여러 DB 벤더 (MySQL, Oracle 등) 마다 SQL 사용이 조금씩 다르기 때문에 어플리케이션 개발 시 처음 선택한 DB를 나중에 바꾸는 것은 매우 어렵다.
      • 하지만, JPA는 추상화된 데이터 접근 계층을 제공하기 때문에 특정 벤더에 종속적이지 않다.
      • 설정 파일에서 JPA에게 어떤 DB를 사용하고 있는지 알려주기만 하면 얼마든지 DB를 변경할 수 있다.
  • 단점
    1. 어렵다.
      • 많은 내용이 감싸져 있기 때문에 JPA를 잘 사용하기 위해서는 알아야 할 것이 많다.
      • 잘 이해하고 사용하지 않으면 데이터 손실이 있을 수 있다.
    2. 성능
      • 메소드 호출로 쿼리를 실행하는 것은 내부적으로 많은 동작이 있다는 것을 의미하므로, 직접 SQL을 호출하는 것보다 성능이 떨어질 수 있다.
    3. 세밀함이 떨어진다.
      • 메소드 호출로 SQL을 실행하기 때문에 세밀함이 떨어진다. 또한, 객체 간의 매핑(Entity Mapping)이 잘못되거나 JPA를 잘못 사용하여 의도하지 않은 동작을 할 수도 있다.
      • 복잡한 통계 분석 쿼리를 메소드 호출로 처리하는 것은 힘들다.
        • 이것을 보완하기 위해 JPA에서는 SQL과 유사한 기술인 JPQL을 지원한다.
        • SQL 자체 쿼리를 작성할 수 있도록 지원도 하고 있다.

3. Spring Data JPA

  • Spring 에서 제공하는 모듈 중 하나로 JPA를 쉽고 편하게 사용할 수 있도록 도와줍니다.
  • 기존에 JPA를 사용하려면 EntityManager을 주입 받아 사용해야 하지만, JPA를 한 단계 더 추상화 시킨 Repository 인터페이스를 제공합니다.
    • Repository의 구현에서 JPA를 사용하고 있다.
  • Repository 인터페이스에 정해진 규칙대로 메소드를 입력하면, Spring이 알아서 해당 메소드 이름에 적합한 쿼리를 날리는 구현체를 만들어서 Bean으로 등록해준다.

Hibernate / Spring Data JPA 차이점

  • Hibernate
    • JPA 구현체
    • 낮은 결합도의 이점을 살린 ORM 프레임워크로써 API 레퍼런스를 제공한다.
  • Spring Data JPA
    • JPA에 대한 데이터 접근의 추상화. Hibernate 말고 Eclipse Link 등의 JPA 구현체를 사용할 수 있다.
    • @Transaction 어노테이션을 통해 트랜잭션 영역을 선언하여 관리할 수 있다.
    • 항상 Hibernate와 같은 JPA 구현체가 필요함.
로깅 라이브러리

https://hoozy.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EB%A1%9C%EA%B7%B8-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-Slf4j

자바 단위 테스트

단위 테스트

  • 특정 소스코드의 모듈이 의도한 대로 작동하는지 검증하는 테스트
  • 함수 및 메소드에 대한 테스트를 하는 작업
  • Spring에서 단위테스트를 하는 것은 스프링 컨테이너에 올라와 있는 Bean들을 테스트 하는 것이다.
  • JUnit을 사용하면 스프링 컨테이너를 띄워, 그 위에 올라가 있는 Bean을 테스트 할 수 있다.

특징

  1. 단정 메소드(assert)로 테스트 케이스의 수행 경과를 판별
    • assert()는 JUnit의 대표적인 기능이다. 이 메소드를 사용하여 테스트가 정상인지 아닌지 판별한다.
    • EX) assertEquals(예상값, 실제값)
  2. JUnit 4 부터는 테스트 어노테이션을 제공
    • EX) @Test @Before @ After
  3. 각 @Test 어노테이션 메소드 호출 시 새로운 인스턴스를 생성하여 독립적인 테스트가 이루어지도록 함.

JUnit 테스트 어노테이션

  1. @Test
    • 메소드에 @Test를 선언 : 테스트를 수행하는 메소드라는 의미
    • JUnit은 각각의 테스트가 서로 영향을 주지 않고, 독립적으로 실행되는 것을 원칙으로 하기 때문에 @Test 메소드마다 객체를 별도로 생성한다.
  2. @Before
    • @Before 어노테이션이 선언된 메소드는 @Test 메소드가 실행되기 전 반드시 실행한다.
    • 보통 공통으로 사용되는 set-up 코드를 @Before 메소드에 선언한다.
    • 각 메소드 전에 실행되는데, 같은 환경에서 여러 메소드를 테스트하는 경우 단위테스트 전체 수행 전 @BeforeClass 어노테이션 선언으로 한 번만 선언하면 된다.
  3. @After
    • @After 어노테이션이 선언된 메소드는 @Test 메소드가 실행된 후 반드시 실행한다. 마찬가지로 전체 수행 후에는 @AfterClass 어노테이션 선언
  4. @Ignore
    • 이 어노테이션을 선언하면 테스트를 skip 한다.

JUnit 테스트 방법

  • Gradle 추가
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
  • @Test 방법
public class Dollar {
    int amount;

    Dollar(int amount) {

    }

    void times(int multiplier) {

    }
}
/////////////////////////////////////////////////////
import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

public class AppTest {
    @Test
    public void testMultiplication() {
        Dollar five = new Dollar(5);
        five.times(2);
        assertEquals(10, five.amount);
    }
}

자바 스프링부트 끝.

다음 게시글 GIT 과 GITHUB

https://hoozy.tistory.com/entry/GIT-GITHUB

 

[백엔드] GIT / GITHUB

이전 게시글 JAVA 프레임워크 https://hoozy.tistory.com/entry/JAVA-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC 카테고리리 : GIT GIT 2005년에 리누스 토르발스라는 리눅스 개발자가 개인이 사용하기 위해 개발한 '분

hoozy.tistory.com

 

 

참고 자료


https://www.codestates.com/blog/content/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8
https://blog.naver.com/PostView.nhn?blogId=wool_ly&logNo=222348528264&parentCategoryNo=&categoryNo=23&viewDate=&isShowPopularPosts=true&from=search
https://dev-coco.tistory.com/74
https://jaeseongdev.github.io/development/2021/01/02/Gradle%EC%97%90%EC%84%9C_Junit5_%EC%85%8B%ED%8C%85%ED%95%98%EB%8A%94_%EB%B0%A9%EB%B2%95/

'CS > 자바' 카테고리의 다른 글

[JAVA] 심화 2  (0) 2023.03.10
[JAVA] 심화 1  (0) 2023.03.08
[JAVA] 기초  (0) 2023.03.07

댓글