데브코스 14일차 TIL

Heesu Song·2025년 3월 23일

데브코스 - 백엔드

목록 보기
10/32
post-thumbnail

오늘 또 눈 때문에 병원을 가야 돼서 중간에 한 시간 수업을 못들었다. 과제 시간 만큼은 제대로 듣고 싶었는데 좀 슬펐다…

그리고 오늘 두번째 RBF를 했는데,
오늘 준비한 내용이 혼자 볼때는 잘 이해가 되지 않았지만 팀원분들이 그 부분에 설명을 추가해줘서 공부에 도움이 많이 됐다! Iterator를 사용하는 이유랑 for-each문과의 차이점이 잘 와닿지 않았었는데 이번에 제대로 정리해보니 머리에 더 잘 들어오는 느낌
매번 느끼지만 다들 정말 아는게 많으시다… 나는 암것도 몰라서 입을 움직일 수 가 없다..🥺

아! 그리고 야구 개막해서 넘 행복하다🧡 🦅 

과제 - Java

OOP답게 클래스를 분리해서 개발하는게 좋다.

VO 와 DTO가 하는일은 비슷하다

  • VO → 값을 집어넣기만 하는 객체 (상자 객체)
  • DTO → 값을 전달해주는 객체 (계층간 값 전달)

Entity → 값을 담아오기 때문에 VO와 비슷한 일을 하긴 하지만 값을 옮겨주는 것도 가능 (하지만 분리해서 사용해도 된다.)

  • 기능에 중복된 코드가 있음→ 똑같은 오류가 발생할 수 있다.

1단계 끝 - CRUD

  • 코드 중복이 많음
  • 기능 분리 필요

ChatGPT보다 직접 서치하면서 공부하는게 실력향상에 도움이 많이 됨

열화판 파라미터

→ 똑같은 파리미터를 여러개 받을 수 있음

→ web에서는 변수를 중복해서 넣으면 list 형식으로 저장

  • startsWith() → 문자열이 어떤 문자로 시작하는지 확인하는 메서드
  • split() → 매개변수에 두번째로 정수를 넣어주면 limit을 둠

병원 갔다 오고 완전히 놓쳤다,, 올려주신 코드 보고 혼자 다시 해 봐야 될듯


과제 - SQL

Q1.

# a5 데이터베이스 삭제/생성/선택
DROP DATABASE a5;

CREATE DATABASE a5;

USE a5;

# 부서(dept) 테이블 생성 및 홍보부서 기획부서 추가
CREATE TABLE dept(

	id INT(5) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    dept VARCHAR(10) NOT NULL UNIQUE

);

INSERT INTO dept (dept) VALUES
    ('홍보부서'),
    ('기획부서');

# 사원(emp) 테이블 생성 및 홍길동사원(홍보부서), 홍길순사원(홍보부서), 임꺽정사원(기획부서) 추가

CREATE TABLE emp (
    id INT(5) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(20) NOT NULL,
    dept_name VARCHAR(20) NOT NULL
);

INSERT INTO emp (name, dept_name) VALUES
    ('홍길동', '홍보부서'),
    ('홍길순', '홍보부서'),
    ('임꺽정', '기획부서');

# 홍보를 마케팅으로 변경

UPDATE emp
SET dept_name = '마케팅부서'
WHERE dept_name = '홍보부서';

# 마케팅을 홍보로 변경

UPDATE emp
SET dept_name = '홍보부서'
WHERE dept_name = '마케팅부서';

# 홍보를 마케팅으로 변경

UPDATE emp
SET dept_name = '마케팅부서'
WHERE dept_name = '홍보부서';

# 구조를 변경하기로 결정(사원 테이블에서, 이제는 부서를 이름이 아닌 번호로 기억)

ALTER TABLE emp DROP COLUMN dept_name;

ALTER TABLE emp ADD COLUMN dept_id INT(5) UNSIGNED NOT NULL;

UPDATE emp SET dept_id = (SELECT id FROM dept WHERE dept = '홍보부서') WHERE name IN ('홍길동', '홍길순');
UPDATE emp SET dept_id = (SELECT id FROM dept WHERE dept = '기획부서') WHERE name = '임꺽정';

ALTER TABLE emp ADD CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id);

# 사장님께 드릴 인명록을 생성

SELECT id AS 사원번호, name AS 이름, dept_id AS 부서번호 FROM emp;

# 사장님께서 부서번호가 아니라 부서명을 알고 싶어하신다.
# 그래서 dept 테이블 조회법을 알려드리고 혼이 났다.

SELECT * FROM dept;

# 사장님께 드릴 인명록을 생성(v2, 부서명 포함, ON 없이)
# 이상한 데이터가 생성되어서 혼남

SELECT e.id AS 사원번호, e.name AS 이름,
       (SELECT dept FROM dept WHERE id = e.dept_id) AS 부서명
FROM emp e;

# 사장님께 드릴 인명록을 생성(v3, 부서명 포함, 올바른 조인 룰(ON) 적용)
# 보고용으로 좀 더 편하게 보여지도록 고쳐야 한다고 지적받음

SELECT e.id AS 사원번호, e.name AS 이름, d.dept AS 부서명
FROM emp e
JOIN dept d ON e.dept_id = d.id;

# 사장님께 드릴 인명록을 생성(v4, 사장님께서 보시기에 편한 칼럼명(AS))

SELECT
    e.id AS '사원 번호',
    e.name AS '이름',
    d.dept AS '부서명'
FROM emp e
JOIN dept d ON e.dept_id = d.id;

→ 상당하다. 상당히 어렵다.

Q2. (시간 될 때마다.. 수정)

# a6 DB 삭제/생성/선택

# 부서(홍보, 기획, IT)

# 사원(홍길동/홍보/5000만원, 홍길순/홍보/6000만원, 임꺽정/기획/4000만원)
## IT부서는 아직 사원이 없음

# 전 사원에 대하여, [부서명, 사원번호, 사원명] 양식으로 출력

# 전 사원에 대하여, [부서명, 사원번호, 사원명] 양식으로 출력
## IT부서는 [IT, NULL, NULL] 으로 출력

# 전 사원에 대하여, [부서명, 사원번호, 사원명] 양식으로 출력
## IT부서는 [IT, 0, -] 으로 출력

# 모든 부서별, 최고연봉, IT부서는 0원으로 표시

# 모든 부서별, 최저연봉, IT부서는 0원으로 표시

# 모든 부서별, 평균연봉, IT부서는 0원으로 표시

🌏 RBF 공부 내용

Iterator

  • Iterator는 Collection에 저장된 요소들을 순차적으로 읽어오기 위해 사용된다. Iterator 인터페이스에 기능이 정의되어 있으며, Collection 인터페이스에는 Iterator()가 정의되어 있다.
    • Iterator() → Iterator 인터페이스를 구현한 클래스의 인스턴스를 반환하는 메서드

Iterator()의 주요 메서드

메서드기능
hasNext()- 다음 요소가 있으면 true, 없으면 false 반환
next()- 다음 요소를 반환
- next()를 호출하기 전에 hasNext()를 통해 읽어올 다음 요소가 있는지 먼저 확인하는게 좋음
remove()- next()를 통해 읽어온 객체를 삭제
- next()를 호출한 다음에 remove()를 사용해야함
  • hasNext() → next() → remove() 순으로 사용

Iterator와 for-each문 비교

요소 접근

  • Iterator는 요소에 접근할 때 next()를 사용하고, for-each는 콜론(:)으로 자동 접근

요소 삭제

  • Iterator는 remove()로 요소 삭제 가능, for-each는 요소 삭제가 불가능
    • 그렇기 때문에 컬렉션을 탐색하면서 요소를 삭제해야 될 때는 Iterator를 사용하는 것이 안전함
→ for-each문에서 요소를 삭제하면 ConcurrentModificationException이 발생할 수 있음

유연성

  • Iterator는 Collection을 직접 수정하면서 순회가 가능, for-each는 Collection 수정이 불가능

Iterator가 필요한 경우

1️⃣ Collection을 탐색하면서 요소를 삭제해야 할 때

  • for - each 문에서는 삭제가 불가능 하지만 Iterator의 remove()는 삭제가 가능

2️⃣ Collection이 List가 아니라 Set이나 Map일 때

  • Set과 Map은 for 문으로 인덱스 접근이 불가능 하므로 Iterator 사용 → Set과 Map은 인덱스가 없기 때문!

3️⃣ 동기화된 환경에서 컬렉션을 안전하게 순회할 때

  • 멀티스레드 환경에서 하나의 컬렉션을 여러 스레드가 동시에 접근하면 동기화 문제가 발생
  • 예를 들어 하나의 스레드가 for-each 문을 사용해 리스트를 순회하는 동안, 다른 스레드가 리스트를 수정하면 컬렉션의 구조가 변경 되면서 예외가 발생할 수 있음

→ 사실 3번은 잘 이해가 되지 않음..

Iterator와 Iterable의 차이

Iterator와 Iterable은 역할사용하는 방식에 차이가 있음

Iterable은 Collection이 Iterator를 제공할 수 있도록 하는 인터페이스 → for-each문에서 사용이 가능하도록 함

  • Iterator 사용 예시
Iterator<String> iterator = list.iterator();

while (iterator.hasNext()) {  
	System.out.println(iterator.next());  
}
  • Iterable 인터페이스
public interface Iterable<T> {
    Iterator<T> iterator();  
}

3️⃣ 동기화된 환경에서 컬렉션을 안전하게 순회할 때

  • 멀티스레드 환경에서 하나의 컬렉션을 여러 스레드가 동시에 접근하면 동기화 문제가 발생
  • 예를 들어 하나의 스레드가 for-each 문을 사용해 리스트를 순회하는 동안, 다른 스레드가 리스트를 수정하면 컬렉션의 구조가 변경 되면서 예외가 발생할 수 있음

→ 사실 3번은 잘 이해가 되지 않음..

이후에 팀원분들이 3번에 대해 적어주신 내용

  • 다음 요소를 하나 씩 접근하기 때문에..?
  • ArrayList의 경우 순회중인 cursor보다 List 사이즈가 작아지면 ConcurrentModificationException을 발생시킴 -> 예외처리 가능
public E next() {
    checkForComodification(); // 비 이터레이터에서 수정시 예외발생
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException(); //존재의미??
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

-----------------
// 위에 next() 메서드 설명 추가

public E next() {
    checkForComodification(); // ✅ 리스트가 변경되었는지 검사 (동시 수정 예외 발생 가능)
    int i = cursor;  // ✅ 현재 위치(cursor)를 가져옴
    if (i >= size)   // ✅ 만약 cursor가 리스트 크기를 넘어서면 예외 발생
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;  // ✅ 내부 배열 참조
    if (i >= elementData.length) // ✅ 리스트가 변경되어 길이가 줄어들었을 경우
        throw new ConcurrentModificationException();  // ❌ 동시 수정 예외 발생!
    cursor = i + 1; // ✅ 다음 요소로 이동
    return (E) elementData[lastRet = i];  // ✅ 현재 요소 반환
}
  • 다른 스레드에서 요소 변화 시 오류 발생 예시
public class Main {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));

        new Thread(() -> {
            try {
                Thread.sleep(500); // 잠시 대기 후 6 추가
                
                list.add(6);
                // list.remove(list.size() - 1);
                System.out.println("다른 스레드에서 요소 변경");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
            try {
                Thread.sleep(1000); // 순회 중 일부러 딜레이 줌
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

동기화된 컬렉션을 메서드 호출할 때는 상관없지만, 리스트를 순회하는 것은 명시적으로 동기화 블록(Synchronized) 사용해야함 -> Iterator자체는 Thread - Safe하지않다.

for-each 동작 원리 - 단일 스레드 기준

  • 내부적으로 Iterator를 사용하여 컬렉션 순회
    • Iterator는 컬렉션 구조 변경을 감지하기 위해 modCount 변수 사용 -> 컬렉션의 구조적 변경을 감지하여 안정성과 일관성을 유지하기 위함 (컬렉션 불변성 보호 목적)
    • Iterator는 생성 시점에 modCount 값을 캡처
    • 순회 중 next() 메서드 호출 시 현재 modCount 값과 캡처된 modCount 값을 비교
    • 두 값이 다른 경우 **ConcurrentModificationException 발생 ->** 컬렉션 구조적 변경이 발생함을 의미
  • for-each 중간 컬렉션의 메서드로 요소가 제거된 경우
    • for-each 문 도중 컬렉션의 메서드 (예: list.remove())를 직접 호출하여 요소를 삭제하는 경우 컬렉션의 modCount 변경
    • 위의 modCount 변경은 Iterator가 캡처한 초기 modCount와 동기화되지 않아 이후 Iterator.next() 호출 시 예외 발생
  • for-each 중간 Iterator의 메서드로 요소가 제거된 경우
    • Iterator는 자체적으로 제공하는 remove() 메서드를 통해 컬렉션의 요소를 안전하게 제거할 수 있음
    • 동작 과정
      • Iterator.remove()로 요소를 제거하며 내부적 컬렉션의 modCount 값 업데이트
      • 컬렉션 modCount 값 업데이트와 동시에 Iterator가 캡처한 초기 modCount 값도 같이 업데이트
      • 컬렉션의 modCount와 Iterator가 초기에 캡처한 modCount가 동기화 되어 예외가 발생하지 않음
  • 다중 스레드 환경에선 명시적인 동기화를 추가하거나 병렬 처리를 지원하는 컬렉션을 사용하자

profile
Abong_log

0개의 댓글