| En

GitHub CLI로 Pull Request 관리

PR을 만들고 CI를 기다리고 리뷰하고 머지하는 흐름은 보통 브라우저 탭을 여러 번 오가게 한다. GitHub CLI(gh)로는 이 과정을 터미널에서 끝낼 수 있다. 설치는 OS별로 다르니 공식 설치 안내를 참고한다(macOS는 brew install gh, Ubuntu/Debian은 sudo apt install gh). 설치 후 인증을 한 번 한다. gh auth login 브라우저 기반 OAuth가 가장 간단하다. 인증 여부는 다음으로 확인한다. $ gh auth status github.com ✓ Logged in to github.com account in-jun (keyring) - Active account: true - Git operations protocol: https - Token scopes: 'gist', 'read:org', 'repo', 'workflow' 1. PR 만들기 먼저 브랜치를 만들고 작업을 push한다. ...

2024년 7월 19일 · 4 분 · 731 단어 · In-Jun

Git 커밋 히스토리 정리

로컬에서 기능 하나를 만들다 보면 커밋 히스토리가 이렇게 쌓인다. f16a902 Add logout feature fbfb5b2 Fix login button style 6cc4fec Add missing import cacd382 Fix typo in login 0559677 Add login feature Fix typo, Add missing import, Fix login button style는 전부 Add login feature에 들어갔어야 할 수정이다. 작업 중에 자주 커밋하는 것 자체는 문제가 없다. 잃어버릴 일이 없고 되돌아갈 지점이 많다. 다만 이 상태로 push하면 리뷰어가 의미 없는 커밋 4개를 읽어야 한다. ...

2024년 7월 13일 · 3 분 · 604 단어 · In-Jun

병합된 Git 브랜치 삭제

PR을 머지하고 나면 로컬에 쓸모없어진 브랜치가 쌓인다. 몇 달 지나면 git branch 출력이 한 화면을 넘기고 작업 중인 브랜치를 찾기 어려워진다. 정리 명령은 간단하다. 무엇을 지워도 안전한지 구분하는 것이 관건이다. -d는 안전하고 -D는 위험하다 로컬 브랜치 삭제 명령은 두 가지다. git branch -d는 이미 병합된 브랜치만 지운다. 병합 안 된 커밋이 남아 있으면 거부한다. $ git branch -d feature/login Deleted branch feature/login (was 13d794b). $ git branch -d feature/experimental error: the branch 'feature/experimental' is not fully merged hint: If you are sure you want to delete it, run 'git branch -D feature/experimental' feature/login은 main에 병합됐으니 바로 지워졌고, feature/experimental은 고유 커밋이 남아 있어 거부됐다. 이 거부가 작업을 실수로 날리지 않게 막는다. ...

2024년 7월 11일 · 3 분 · 513 단어 · In-Jun

플로이드–워셜 알고리즘

최단 경로 알고리즘은 보통 한 정점에서 나머지 전부로 가는 거리를 푼다. 다익스트라와 벨만-포드 모두 시작점이 하나다. 모든 정점에서 모든 정점으로 가는 거리가 필요하면 다익스트라를 V번 돌릴 수도 있지만, 우선순위 큐와 인접 리스트를 매번 다시 세팅해야 하고 음수 가중치가 있으면 쓸 수 없다. 플로이드-워셜은 시작점을 고정하지 않는다. 거리 행렬 하나를 놓고 3중 루프를 한 번 돌면 모든 쌍의 최단 거리가 행렬에 채워진다. 코드는 12줄이고, 음수 간선을 처리하며, 음수 사이클의 존재 여부까지 알려준다. ...

2024년 6월 17일 · 7 분 · 1326 단어 · In-Jun

JPA Dirty Checking 동작

조회한 엔티티의 setter만 호출하고 트랜잭션을 끝내면 DB가 바뀐다. save()나 update()를 부르지 않았는데도 그렇다. 이 자동 UPDATE를 만드는 동작이 Dirty Checking, 즉 변경 감지다. Hibernate가 어떤 필드가 바뀌었는지를 무엇과 비교해 알아내는지가 동작의 핵심이다. 스냅샷 비교의 기준은 스냅샷이다. 엔티티가 영속성 컨텍스트에 처음 등록되는 순간 Hibernate는 모든 필드 값을 복사해 별도로 보관한다. 이게 DB와 일치하는 최초 상태다. 내부적으로는 엔티티 식별자를 키로 하고, 각 필드 값을 순서대로 담은 Object 배열을 값으로 갖는다. 스냅샷은 두 시점에 만들어진다. find()나 JPQL로 DB에서 엔티티를 조회할 때, 그리고 persist()로 새 엔티티를 영속화할 때다. merge()는 조금 다르다. 준영속 엔티티의 값을 영속 엔티티에 복사한 뒤 그 영속 엔티티에 대한 스냅샷을 만들고, 그 영속 엔티티를 반환한다. 원본 준영속 엔티티는 그대로 준영속 상태로 남는다. ...

2024년 6월 8일 · 4 분 · 733 단어 · In-Jun

JPA N+1 쿼리 문제

회원 목록을 한 번 조회했는데 SQL 로그에 SELECT가 101줄 찍힌다. 이것이 N+1 문제다. 연관 엔티티를 가져오는 첫 쿼리 1개에, 그 연관을 건드릴 때마다 추가로 N개가 더 나가 총 N+1개가 된다. 추정 밀리초가 아니라 로그에 찍힌 쿼리 개수가 이 문제의 신호다. 1개가 101개가 되는 과정 Member가 Team을 지연 로딩(FetchType.LAZY)으로 참조한다고 하자. 회원 100명을 조회하고 반복문에서 각 회원의 팀 이름을 출력한다. List<Member> members = memberRepository.findAll(); // 쿼리 1개 for (Member member : members) { System.out.println(member.getTeam().getName()); // 여기서 팀마다 쿼리 1개 } 첫 줄에서 회원을 한 번에 가져온다. ...

2024년 6월 8일 · 4 분 · 659 단어 · In-Jun

Spring Data JPA와 JPA의 차이

JPA는 표준 API이고, Spring Data JPA는 그 위에 얹힌 코드 생성 계층이다. 인터페이스만 선언하면 구현을 만들어 주고, 그 구현이 내부에서 EntityManager를 호출한다. 같은 동작을 두 방식으로 작성하면 경계가 드러난다. JPA JPA의 중심은 영속성 컨텍스트와 그것을 다루는 EntityManager다. 영속성 컨텍스트는 엔티티를 보관하는 논리적 공간으로 1차 캐시, 변경 감지(Dirty Checking), 지연 로딩, 쓰기 지연을 제공한다. 순수 JPA에서는 EntityManager를 직접 잡고 트랜잭션도 직접 열고 닫는다. EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user = new User("홍길동", "[email protected]"); em.persist(user); // 영속 상태로 전환 → INSERT 예약 User found = em.find(User.class, user.getId()); // 1차 캐시에서 반환 (추가 쿼리 없음) found.setName("김철수"); // 변경 감지 → flush 시 자동 UPDATE tx.commit(); // 예약된 SQL 실행 em.close(); 동작은 하지만, 모든 엔티티의 모든 CRUD마다 createEntityManager, getTransaction, begin, commit, close가 반복된다. Spring Data JPA가 제거하는 것이 이 반복이다. ...

2024년 6월 7일 · 4 분 · 659 단어 · In-Jun

Spring Interceptor 동작

인터셉터가 필터와 다른 점은 위치다. 인터셉터는 DispatcherServlet 안에서, 핸들러 매핑이 어느 컨트롤러가 요청을 처리할지 결정한 뒤에 실행된다. 따라서 인터셉터는 어떤 핸들러가 곧 실행될지 이미 안다. 그 실행을 컨트롤러 호출 전, 호출 후, 뷰 렌더링 후 세 지점에서 가로채고, 첫 지점에서는 요청 자체를 막을 수 있다. 세 개의 훅 HandlerInterceptor는 가로채는 시점이 다른 세 메서드를 제공한다. preHandle preHandle은 컨트롤러 실행 전에 호출되고 boolean을 반환한다. true면 다음 인터셉터나 컨트롤러로 진행하고, false면 요청 처리가 즉시 중단되어 컨트롤러는 실행되지 않는다. 인증 확인, 권한 검증, 호출 횟수 제한 같은 게이트키퍼 역할에 맞는 이유다. ...

2024년 6월 4일 · 4 분 · 643 단어 · In-Jun

서블릿 Filter와 요청 처리

필터는 요청이 들어왔을 때 가장 먼저 손대는 자리다. DispatcherServlet도, 컨트롤러도, 스프링 빈도 아직 없다. 서블릿 컨테이너(Tomcat 등) 레벨에서 동작하기 때문이다. 이 위치가 필터의 성격을 결정하고, CORS 처리와 보안 토큰 검증이 인터셉터가 아니라 필터에 사는 이유를 설명한다. 서블릿(Servlet) 클라이언트의 HTTP 요청을 처리하고 응답을 생성하는 자바 서버 측 컴포넌트. Java EE(현 Jakarta EE) 표준의 핵심이며 대부분의 자바 웹 프레임워크의 기반이다. 필터가 잡는 범위 필터의 적용 범위는 모든 요청이다. 컨트롤러로 가는 요청만이 아니라 정적 리소스 요청도, 잘못된 경로로 들어와 에러로 빠지는 요청도 전부 필터를 통과한다. DispatcherServlet 앞단에 있기 때문이다. 예외 없이 모든 요청에 한 번은 적용해야 하는 일이 필터의 영역이다. 문자 인코딩 통일, CORS 헤더, 보안 토큰 검증, 응답 압축, 요청/응답 로깅이 여기 해당한다. ...

2024년 6월 4일 · 4 분 · 681 단어 · In-Jun

웹 인증 Cookie, Session, JWT

웹 인증(Web Authentication)은 HTTP 프로토콜의 무상태(Stateless) 특성 때문에 발생하는 사용자 식별 문제를 해결하기 위한 핵심 메커니즘이다. 1994년 Netscape Communications의 Lou Montulli가 쿠키를 발명한 이후 인증 방식은 세션 기반 인증과 토큰 기반 인증으로 발전해왔고, 오늘날에는 보안성과 확장성을 함께 고려해 JWT와 Refresh Token을 조합한 하이브리드 방식이 널리 사용된다. 인증과 인가의 개념 인증(Authentication)과 인가(Authorization)의 차이 인증(Authentication)은 “당신이 누구인가?“를 확인하는 과정으로 사용자의 신원을 검증하는 것이며, 인가(Authorization)는 “당신이 무엇을 할 수 있는가?“를 결정하는 과정으로 인증된 사용자에게 특정 리소스에 대한 접근 권한을 부여하는 것이다. 인증이 먼저 수행되어야 인가가 가능하며, 두 개념은 명확히 구분되어야 한다. ...

2024년 6월 2일 · 7 분 · 1470 단어 · In-Jun
[email protected]