Java_컬렉션

ChoRong0824·2023년 5월 14일
0

Java

목록 보기
18/31
post-thumbnail

Java_컬렉션에 대해서 문제위주 학습한 것을 포스팅하였습니다.
최대한 이해하기 쉽게 설명해보았습니다.


컬렉션

컬렉션 프레임워크란 무엇인지 개념을 설명

// [1] : Collection
// List 계열 구현 클래스→________________, _______________, _____________, ____________
// Set 계열 구현 클래스→________________, _______________, _____________


// [2] : List vs Set
// 컬렉션 프레임워크의 핵심 인터페이스들.
// List 인터페이스를 상속하는 클래스 특징→___________________________________________________________________________ /
/ Set 인터페이스를 상속하는 클래스 특징→_________________________________________________

--> 밑줄친 부분에 무엇이 들어가야할지 맞춰봅시다.


사전학습

아무 자바 책의 컬렉션 파트를 봤을 때, 꽤 어렵네.. 라고 느낀다면? --> 선수학습이 안되어 있어서 그렇습니다.

이것은, OOP, 추상클래스, 상속, 인터페이스 를 무조건. 남한테 설명할 수 있을정도로 학습을 해야합니다.
(해당 포스트가 이해가 안될 경우, 이전 포스팅들을 읽어보시는 것을 권장드립니다)
사전학습이 안되어 있으면, 컬렉션을 이해하기 어렵습니다.

필자는 컬렉션에 대한 개념과 ArrayList 클래스 위주로 포스팅 할 예정입니다.


컬렉션 프레임워크란 ?

"수집품"이라는 사전적 의미에서 유추할 수 있듯이 "데이터 값들을 담는 여러 그릇" 정도로 이해하시면 됩니다.

옷장, 서랍장, 수납장을 열어보면 종류 및 용도별로 도구가 많이 있듯이, 어떤 자료구조 데이터를 저장하고 관리할 것인지에 따라서 맞는 도구를 써야합니다.

  • 용도에 맞는 도구를 써야지만, 보기도 좋고, 작업도 효율적으로 진행됩니다.
    효율 x--> 그릇이 없어서 밥 그릇에 국물을 떠오고,
    그릇이 없어서 간장을 국 그릇에 떠온다.

즉, 내가 만들 프로그램의 데이터 처리를 어떻게 할지 그 특징을 잘 파악하고 컬렉션내 맞는 그릇(클래스)를 잘 골라써야 합니다.

컬렉션 프로그레임워크는 크게 봤을 때 --> CollectionMap로 나뉘고 -->이 둘은 모두 인터페이스(Interface)로 되어 있습니다. ( Interface가 기억 안난다면 복습 추천 )

Collection은 또 ListSet로 나뉘고 --> 이들도인터페이스 --> 이들을 상속받아 다양한 형태의 배열(그릇,클래스)로 사용됩니다.


Collection --> 상속 --> Listset 으로 구분(Interface) --> 상속 -->
List 계열 구현 클래스 Vs Set 계열 구현 케이스
( 어떻게 데이터를 처리할 것인지 용도를 정해졌다면, 용도에 맞게 사용 )
Set은 중복 허용 x , List는 중복 허용

필수 암기

List 계열 구현 클래스 --> ArrayList, LinkedList, Vector, Stack
Set 게열 구현 클래스 --> HashSet, SortedSet, TreeSet


List vs Set

컬렉션 프레임워크의 핵심 인터페이스들

List 인터페이스를 상속하는 클래스들 특징

  1. 인덱스가 있고,
  2. 그래서 저장 순서가 유지되고,
  3. 데이터 중복이 허용

Set(집합) 인터페이스를 상속하는 클래스들 특징

  1. 데이터 중복이 허용 안됨

ArrayList

먼저, 자바의 배열은 크기를 미리 지정하고 사용했다. --> 그러다 보니 넉넉하게 크기를 지정해놓고 사용하곤 한다.(지정한 크기만큼만 사용하기 때문에, 유연하지 못함)

반면, ArrayList는 필요시 언제든지 추가, 삭제가 가능합니다. (미리 크기 지정 x)

List 인터페이스를 상속하므로 인덱스가 있고, (인덱스 있다는 말은) 저장 순서가 유지되고, 데이터 중복이 가능하다.

또한, 제네릭 문법을 사용(선택사항)할 수 있다. --> 만약, 제네릭을 사용하지 않는다면 내부적으로 _________ 타입으로 처리된다. (Object)

사용을 위해서는 상단에 import가 필요합니다. --> import java.util.ArrayList;

Code
import java.util.*;
class Simpson{ }

public class Collection {
    public static void main(String[] args) {
        // [1] : ArrayList 를 제네릭이 아닌 Object 타입으로 사용하는 경우
        ArrayList list1 = new ArrayList();

        // [2] : 데이터 추가하기 --> add()
        list1.add("홍길동"); // 문자열 자료형 저장
        list1.add(1000); // 정수 자료형 저장
        list1.add("이순신");
        list1.add(1000);  // 데이터 중복 가능
        list1.add(new Simpson()); // 이렇게 객체 생성해줘도, 전혀 에러가 안남. --> 오브젝트 타입으로 처리되기 때문에 받아줄 수 있음.

        System.out.println("---------------------------------------");
        // [3] : 데이터 가져오기 --> get() --> 이때, 해당 데이터 자료형으로 형 변환하여 사용한다.
        // (중요한 것이 생김, 가져와서 사용할 때엔 형변환 해줘야함)
        System.out.println(list1.get(0)); // 홍길동 --> ????
        // String str = list1.get(0); // 이렇게 저장할 수 없음. --> (Object 타입으로 사용되고, 타입으로 처리(반환) 되므로)
        String str = (String)list1.get(0); // 형변환 해줘야 사용 가능함.
        System.out.println(str.length()); // 길이는 length() 메서드 사용해줌.
        int num = (Integer) list1.get(1); // 정수타입의 형변환 해줘야함. (int)로 작성가능
        System.out.println(num+100);

        // [!] : 결론
        // 이상으로 봤을 때, 제네릭 문법을 사용하지ㅣ 않으면 ArrayList는 내부적으로 Object 타입으로 처리됨을 알 수 있다.
        // 이렇게 `get()` 메서드를 사용할 때는 형변환을 주의해야 한다.
        // --> 제네릭을 사용하면 된다.
        
        // [4] : 출력 --> 반복문 --> 배열의 크기(size) --> 객체명.size()
        System.out.println(list1.size()); // 5
        for (int i = 0; i < list1.size(); i++) System.out.print(list1.get(i)+"\t");
    }
}

ArrayList를 이용한 자료의 추가, 수정, 삭제, 출력을 구현

출력
을지문덕
징키스칸 
--------------------[삭제 후 출력] 
홍길동
이순신
강감찬
김유신
--------------------[삭제 후 출력]

코드
import java.util.*;

public class Collection2 {
    public static void main(String[] args) {
        // [0]  : ArrayList 객체 생성 --> 제네릭 사용
        ArrayList<String> str1 = new ArrayList<String>(); // `<String>` : 제네릭 사용

        // [1] : 추가 --> add()
        str1.add("홍길동");
        str1.add("이순신");
        str1.add("강감찬");
        str1.add("을지문덕");
        str1.add("김유신");
        str1.add("홍길동");
        for (int i = 0; i < str1.size(); i++) {
            System.out.println(str1.get(i));
        }
        System.out.println("----------------------------------");
        String str = str1.get(0); // String str = (String)str1.get(0) 으로 형변환 할 필요 없이 바로 사용가능. --> 타입 안전성 증가.
        // 제네릭을 사용하면 형변환을 잘못(실수)헀어도, 컴파일 단계에서 확인할 수 있게 됩니다.
        // int num = (int)str1.get(0); --> 컴파일 단계에서 오류 발견됨. (실행이랑은 다름)
        // --> 제네릭 사용하지 않는다면, ClassCast 발생함.

        // [2] : 수정 --> set 메서드 사용 --> set(인덱스, 수정값)
        str1.set(3, "세종대왕");
        System.out.println(str1.get(3)); // 세정대왕

        // [3] : 삭제 --> 2가지 ( 하나만 삭제 / 한꺼번에 삭제 ) --> remove(인덱스) -- > 하나만 삭제
        str1.remove(3);
        System.out.println("----------------삭제 후 출력 ----");
        for (int i = 0; i < str1.size(); i++) {
            // System.out.println(str1[i]); --> ERR. --> get()메서드를 사용해야함. --> get 메서드는 안에 `[]`이 아닌, `()` 사용함.
            System.out.println(str1.get(i)); // 인덱스 3번이 삭제되고, 삭제된 위치에 김유신
        }
        System.out.println("----------------삭제 후 출력 ----");
        
        // [4] : 출력 --> 향상된 for 문
        for (String str4: str1) // 배열명
            System.out.println(str4);
		
        // 출력 2
        for (int i = 0; i< str1.size(); i++) {
            System.out.printf("%d번 학생의 이름은 %s 입니다.\n", (i+1), str1.get(i));
        }
        
        // [6]: 한꺼번에 삭제 --> removeAll(배열명)
        str1.removeAll(str1);
        System.out.println(str1.size()); // 0
        System.out.println("----------------------------전체 삭제 후 출력");
        
        for (String str2 : str1)
            System.out.println(str2);
        System.out.println("----------------------------전체 삭제 후 출력");
    }
}

컬렉션 프레임워크의 ArrayList 기반으로 2차원 배열을 만들어 요소를 추가하고 출력

hint, 제법 까다로운 문제로서, ArrayList를 이용한 2차원 배열에 대한 개념을 잘 알고 있는지
결과
---------------------------------[전체 요소 출력] 11 12 13 14
21 22 23 24
31 32 33 34 ---------------------------------[전체 요소 출력]
(참고로, 2차원배열 및 배열에 관해 선행학습이 되어있어야합니다)

입력 code
import java.util.*;

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

        // [1] : 객체생성
        ArrayList<Integer[]> arr = new ArrayList<Integer[]>(); // 제네릭 `<>`사용한 객체생성, 정수형 배열을 타입으로 받음.
        // arr.add( ?????????? ) --> ???가 포인트. 배열안에 배열이 있다 --> 2차원 배열
        // 각각의 add() 메서드를 사용해서, 하나의 행이 계속 추가가 되는 것임.
        // 즉, ArrayList 라는 큰 호텔(틀)에 배열을 add() 메서드를 통해 1개의 층을 추가해주거임, 이때, 배열이면 타입에 `[]`을 추가.

        // [2] : 요소 추가 --> add () 함수(Method) 사용
        arr.add(new Integer[]{11, 12, 13, 14}); // 순서 : new -> 타입 : Integer[] -> 초기화 :{ }; --> 0 번째 방의 요소들.
        arr.add(new Integer[]{21, 22, 23, 24});
        arr.add(new Integer[]{31, 32, 33, 34});
    }
}
(초기)
(나중)
즉, 호텔에 층을 추가한다고 생각하면 쉬움. --> 층을 추가하면, 방들도 추가해야함.

출력 code
import java.util.ArrayList;

public class CollectionArrayList {
    public static void main(String[] args) {
        // [1] : 객체생성
        ArrayList<Integer[]> arr = new ArrayList<Integer[]>();

        // [2] : 요소추가
        arr.add(new Integer[]{11, 12, 13, 14});
        arr.add(new Integer[]{21, 22, 23, 24});
        arr.add(new Integer[]{31, 32, 33, 34, 35});

        // [3] : 요소출력
        for (int i = 0; i < arr.size(); i++) {
            System.out.println(arr.get(i)[0]); // 1열 출력함. 11(0번째배열 첫번째 값), 21(1번째배열 첫번째 값), 31(2번째배열 첫번째 값)
        }
        // [4] : 전체 요소 출력 --> 이중 반복문 --> But, 그전에 안쪽 배열의 사이즈를 미리 체크 --> 여기서 약간 주의 !
        // 안쪽 배열의 사이즈 : 4  -- > (1) arr.get(0).size(); --> ERR. --> 컬렉션 프레임워크 타입의 길이를 알고 싶을 때 사용.
        // (2) arr.get(0).length(); --> ERR. --> 문자열의 길이를 알고 싶을 때 사용.
        // 안쪽 배열의 사이즈를 알고 싶다면, (3) arr.get(0).length; 를 해야 알 수 있습니다. --> 배열(int[], Integer[], String[])의 길이를 알고 싶을 때 사용함
        
        System.out.println("-----------------------------------------------전체 요소 출력");
        for (int i = 0; i < arr.size(); i++) {
            for (int j = 0; j < arr.get(i).length; j++) { // 4 이렇게 작성해줘야 열의 길이가 다 출력됨.
                // 만약, j < arr.size(); 를 해준다면, 3열까지만 출력됨. --> 주의 !
                System.out.print(arr.get(i)[j]+"\t");
            }
            System.out.println();
        }
        System.out.println("-----------------------------------------------전체 요소 출력");
    }
}
바깥 배열은 size(), 안쪽은 length 사용

Iterator (반복자)란 ?

사전적의미 --> '반복자'라는 의미에서 어느정도 유추할 수 있듯이 Collection 에 대한 '반복자'입니다.

  • 컬렉션 프레임워크 내에는 다양한 컬렉션들(배열 등)이 있는데, 요소(=원소)를 읽어올 때 Iterator 인터페이스로 표준화 하고 있습니다.
  • 보통 for 반복문을 사용하여 순회할 때 --> index 로 각 요소를 순회하나 --> Iterator(반복자)를 이용하면 조금 더 편리하게 할 수 있습니다.
  • 즉, Iterator(반복자)는 인터페이스다 --> 그래서, 인터페이스내 선언된 메서드들이 있다 --> 주요 메서드 암기 !

  • hasNext(), next(), remove() --> 반환타입은 --> boolean, Object(제네릭), void
  • hasNext() --> 다음 요소가 있는지를 검사하여 true 를 리턴,
  • next() --> 다음 요소를 리턴 --> 그런 후, 다음 위치로 커서가 이동 --> Iterator 에서는 삭제하지 않고, 커서만 이동
  • remove() --> 제거

  • next() 메서드의 경우 --> 배열에 값이 없을때 사용하면 -->주의 !!, ERR. 당연히 오류 발생.
  • 따라서, 사전에 hasNext() 메서드를 사용하여 --> 다음 요소가 있는지 확인 후 --> next() 메서드로 안전하게 요소를 가져온다.
  • 임포트가 필요 --> import java.util.Iterator;
Iterator은 Interface

Iterator 개념과 이를 이용하여 ArrayList 요소를 순회 및 삭제하는 코드를 구현

힌트 : Iterator에 대한 개념과 사용법 그리고 ArrayList에서 사용할 수 있는지

결과 출력

Alligator
Hippo
Ostrich
Donkey
------------------------------[Iterator(반복자)로 출력]
하마 삭제
------------------------------[Iterator(반복자)로 출력]
Alligator
Ostrich
Donkey

Iterator 메서드 예시 Code
		// [4] : Iterator(반복자) 메서드 사용 --> hasNext(), next(), remove()
        System.out.println(iter.hasNext());     // true --> 왜 ? --> 첫 번째 요소인 악어가 있으니깐.. --> 첫 번째요소 확인
        System.out.println(iter.next());        // Alligator
        System.out.println(iter.hasNext());     // true --> 왜 ? --> 두 번째 요소인 하마가 있으니깐..(요소가 있으면 true, 없으면 false)
        System.out.println(iter.next());        // Hippo
        System.out.println(iter.hasNext());     // true --> 왜 ? --> 세 번째 요소인 타조가 있으니깐..(요소가 있으면 true, 없으면 false)
        System.out.println(iter.next());        // Ostrich
        System.out.println(iter.hasNext());     // true --> 왜 ? --> 네 번째 요소인 당나귀가 있으니깐..(요소가 있으면 true, 없으면 false)
        System.out.println(iter.next());        // Donkey

        System.out.println("--------------");
        System.out.println(iter.hasNext());     // false
        // System.out.println(iter.next());        // ERR

        System.out.println("------------------------------------------");
        // [5] : 요소 출력 --> 향상된 for 문
        for(String s:list)
            System.out.println(s);

code
import java.util.ArrayList;
import java.util.Iterator;

public class ArrayListWithIterator {
    public static void main(String[] args) {
        // [1] : 객체생성
        ArrayList<String> list = new ArrayList<String>(); // 뒤쪽엔 타입 생략가능. new ArrayList<>(); 이런식으로

        // [2] : 요소 추가 --> add()
        list.add("Alligator");
        list.add("Hippo");
        list.add("Ostrich");
        list.add("Donkey");

        // [3] : Iterator(반복자) 객체 생성 --> 객체 생성 과정도 중요 !
        // System.out.println(hasNext());       // ERR. --> 반복자 객체를 먼저 생성해야함.
        // Collection 인터페이스 --> iterator() 메서드를 정의하고 있고 --> 이를 상속받는게 List, SEt 인터페이스이므로,
        // List, Set 인터페이스를 상속받아 구현한 클래스들 객체를 통해서 iterator() 메서드를 사용할 수 있음. (사용해서 객체를 생성)
        Iterator<String> iter = list.iterator(); // 위의 list 를 이용해서, 객체 생성

        // [6] : 요소 출력 --> while 사용
        // (문제 속의 문제1) --> 배열 요소 전체를 출력해보세요
        // (문제 속의 문제2) --> 아래의 출력 결과를 예상하여 말해보세요 ? --> ERR 인지 아닌지 && 출력 결과
        // (문제 속의 문제3) --> Hippo 요소만 출력
        // (문제 속의 문제4) --> Hippo 요소만 삭제
        
        System.out.println("---------------------------------[Iterator(반복자)로 출력]---------");
        while (iter.hasNext()) {
            System.out.println(iter.next());
        }
        System.out.println("---------------------------------[Iterator(반복자)로 출력]---------");
    }
}

문제속의 문제 1, 2
import java.util.ArrayList;
import java.util.Iterator;

public class ArrayListWithIterator {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>(); 
        
        list.add("Alligator");
        list.add("Hippo");
        list.add("Ostrich");
        list.add("Donkey");
        
        Iterator<String> iter = list.iterator();
        
        System.out.println(iter.hasNext());     
        System.out.println(iter.next());       
        System.out.println(iter.hasNext()); 
        System.out.println(iter.next());        
        System.out.println(iter.hasNext()); 
        System.out.println(iter.next());        
        System.out.println(iter.hasNext());    
        System.out.println(iter.next());    
        System.out.println(iter.hasNext()); 
        
        System.out.println("---------------------------------[Iterator(반복자)로 출력]---------");
        while (iter.hasNext) {
            System.out.println(iter.next());
        }
        System.out.println("---------------------------------[Iterator(반복자)로 출력]---------");

    }
}
정답
  1. ERR. while 문 안에 hasNext()로 수정시 정상 작동
  2. 아무것도 출력안됨 --> 위에 System.out.println(iter.hasNext()) 부분에서 false 값을 리턴하기 때문에, while 문 자체를 실행하지 않음.

문제속의 문제 3
while (iter.hasNext()) {
        String str = iter.next();
            if ("Hippo".equals(str)) 
            	System.out.println(str);
        }

문제속의 문제 4

(내풀이)

while (iter.hasNext()) {
        String str = iter.next();
            if ("Hippo".equals(str)) continue;
            	System.out.println(str);
        }

(정답)

while (iter.hasNext()) {
		String str = iter.next();
            if ("Hippo".equals(str))
                iter.remove();
            System.out.println("하마 삭제");
        }


이렇게 하마 삭제가 4번이나 반복되는 것을 확인할 수 있습니다.
이것을 1번만 출력되게끔 코드를 수정해주세요.

  • 코드
System.out.println("---------------------------------[Iterator(반복자)로 출력]---------");
        while (iter.hasNext()) {
            // System.out.println(iter.next());
            String str = iter.next();
            if ("Hippo".equals(str))
                iter.remove();
            System.out.println("하마 삭제");
        }
        System.out.println("---------------------------------[현재 리스트 출력]---------");
        for (String s: list)
            System.out.println(s);
정답
System.out.println("---------------------------------[Iterator(반복자)로 출력]---------");
        while (iter.hasNext()) {
            String str = iter.next();
            if ("Hippo".equals(str)){
                iter.remove();
                System.out.println("하마 삭제");
            }
        }
        System.out.println("---------------------------------[현재 리스트 출력]---------");
        for (String s: list)
            System.out.println(s);

Iterator(반복자)를 쓰는 이유

출력

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
	at java.util.ArrayList$Itr.next(Unknown Source)
	at Java100_FrameworkArrayListWithIteratorWhyUse.main(Java100_FrameworkArrayListWithIteratorWhyUse.java:50)
hint) 컬렉션 프레임워크에서 Iterator를 쓰는 이유에 대해서 알고 있는지 확인하는 문제입니다.

당연하게도, 쓰라고 만들었습니다. ㅋㅋ

이런 에러 메시지 때문에 씁니다.

이유

  • 컬렉션 프레임워크를 쓸 때, 한번 이상은 꼭 만나게 되는 에러 메시지
    --> java.util.ConcurrentModificationException
    --> 이 오류 메시지는 보통 컬렉션 list 요소를 반복문 안에서 돌리면서 값을 삭제하고자 할 때, 발생합니다. (삭제 아니여도 다양한 상황에서 발생할 수 있음)

  • 보통, 반복문 안에서 remove() 메서드를 호출할 때 발생합니다.
    그 외에도, Iterator(반복자) 객체의 생성 순서에 따라서도 발생 --> ERR 이유 다양함.

  • 한 행씩 삭제하는 경우에는 별 문제가 없으나 --> 반복문 안에서 순회하면서 삭제시에는..
    반복문 들어가기 전의 기존 list 배열의 size(length)나 index 정보 등이 변경되면서 --> 순회시 정보가 맞지 않아 오류가 발생함

  • 이를 해결하기 위해서는 --> Iterator (반복자)를 사용하여 순회하고 사용하면서 --> 그때, iter.remove() 메서드로 처리해야 합니다.

코드로 확인

import java.util.*;

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

        // [1]: 객체생성
        ArrayList<Integer> list = new ArrayList<>();

        // [2] : 요소 추가 --> add()
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);

        // [3] : Iterator(반복자) 객체 생성
        Iterator<Integer> iter = list.iterator();

        // [4] : 출력 --> 요소 삭제 전
        for (Integer num: list)
            System.out.print(num + " ");
        System.out.println();

        // [5-1] : 요소 삭제 --> 반복문 없이 한 개의 요소만 삭제
        System.out.println(list.get(1));    //2
        // iter.remove(); --> 주의 !!!! --> ERR. --> list.remove() 해줘야함.
        list.remove(1);               //2번 삭제
        System.out.println(list.get(1));    // ??? --> 3

        // [5-2] : 요소 삭제 --> while 반복문 사용
        System.out.println("------------------------------------------------");

        for (Integer i : list)  // for 문은 2개 이상이면 무조건 {} 사용해줘야함.
            list.remove(i);     // ERR --> 이미지 ERR 처럼 발생 --> 반복문 내에 삭제되면 발생 (정보가 바뀌면서) --> 이를 해결하기 위해 iterator 사용
            //System.out.print(i+" ");  출력이라서 별 문제없음.
        System.out.println();

        System.out.println("------------------------------------------------");
    }
}

핵심코드, iterator 사용

import java.util.*;

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

        // [1]: 객체생성
        ArrayList<Integer> list = new ArrayList<>();

        // [2] : 요소 추가 --> add()
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);

        // [3] : Iterator(반복자) 객체 생성
        Iterator<Integer> iter = list.iterator();

        // [4] : 출력 --> 요소 삭제 전
        for (Integer num: list)
            System.out.print(num + " ");
        System.out.println();

        System.out.println("------------------------------------------------");
        /*
        for (Integer j : list) {
            if (j ==2) list.remove(j);
        }
        */
        
        // 핵심 코드
        while (iter.hasNext()) {
            Integer i = iter.next();
            if (i ==2){
                // list.remove(i); // list 가 아니라 iter.remove() 로 해줘야함.
                iter.remove(); //
                System.out.println(i+"번 삭제");
            }
        }
        System.out.println("------------------------------------------------");
        // [6] : 출력 --> 요소 삭제 후
        for (Integer num : list)
            System.out.print(num +" ");
        System.out.println();
    }
}
profile
컴퓨터공학과에 재학중이며, 백엔드를 지향하고 있습니다. 많이 부족하지만 열심히 노력해서 실력을 갈고 닦겠습니다. 부족하고 틀린 부분이 있을 수도 있지만 이쁘게 봐주시면 감사하겠습니다. 틀린 부분은 댓글 남겨주시면 제가 따로 학습 및 자료를 찾아봐서 제 것으로 만들도록 하겠습니다. 귀중한 시간 방문해주셔서 감사합니다.

0개의 댓글