전체 학습 맵 📚 전체 맵
Reference · 용어사전

GLOSSARY

학습 중 나오는 개념들을 모아둔 곳. 검색하거나 카테고리로 찾아보세요.

전체
웹 기초
Spring
DB / JPA
인증 / 보안
Java
HttpSession
HTTP Session
인증 / 보안
로그인 정보를 서버 메모리에 저장해서 로그인 상태를 유지하는 방식. 로그인 성공 시 서버가 세션을 생성하고 브라우저에 JSESSIONID 쿠키를 발급한다. 이후 요청마다 브라우저가 이 쿠키를 보내면 서버가 세션을 찾아 누구인지 확인한다.
// 로그인 성공 시 세션에 저장 session.setAttribute("loginUser", user); // 이후 요청마다 꺼내서 확인 User loginUser = (User) session.getAttribute("loginUser"); if (loginUser == null) return "redirect:/user/login";
단점: 인증 체크 코드를 모든 Controller 메서드마다 반복 작성해야 한다. 서버가 여러 대일 때 세션 공유 문제도 발생한다. → 2차에서 JWT로 전환하는 이유.
HttpSession 완전정복
HttpSession — How It Really Works
인증 / 보안
한 줄 정의: 서버가 클라이언트(브라우저)를 기억하기 위해 서버 메모리에 데이터를 저장하고, 브라우저에게 JSESSIONID 라는 열쇠를 줘서 "너가 누구인지" 매 요청마다 확인하는 메커니즘.
🔍 원론: HTTP는 왜 세션이 필요한가?

HTTP는 Stateless(무상태) 프로토콜이다. 요청이 끝나면 서버는 클라이언트를 즉시 잊는다.

즉, 로그인 후 게시판 페이지로 이동하면 서버 입장에서는 새로운 낯선 사람이 온 것과 같다.
"방금 로그인했잖아요!" — 서버는 모른다. 이걸 해결하기 위한 것이 세션(Session)이다.
🗺 전체 흐름 한눈에 보기
1
로그인 요청
브라우저가 POST /login + 아이디/비밀번호 전송
2
서버 — 인증 확인 후 세션 생성
비밀번호 검증 성공 시 서버 메모리에 세션 객체 생성 → session.setAttribute("loginUser", user)
3
서버 → 브라우저: JSESSIONID 쿠키 발급
응답 헤더에 Set-Cookie: JSESSIONID=ABC123XYZ 포함해서 전송
4
이후 모든 요청 — 브라우저가 쿠키 자동 첨부
요청 헤더에 Cookie: JSESSIONID=ABC123XYZ 자동 포함 (브라우저가 알아서 함)
5
서버 — 세션에서 사용자 꺼내 확인
session.getAttribute("loginUser") → null이면 로그인 페이지로 redirect
🎯 쉬운 비유 — 놀이공원
① 입장권 구매(로그인) → 직원이 내 정보를 장부(서버 세션)에 기록
② 손목 도장(JSESSIONID) → "오늘 입장했음"을 증명하는 도장 찍어줌
③ 어트랙션 탈 때마다 → 직원이 손목 도장 확인 후 장부에서 내 정보 조회
④ 퇴장(로그아웃) → 장부에서 내 정보 삭제, 도장 무효화
💻 실제 코드 흐름
① 로그인 성공 시 — 세션에 저장
@PostMapping("/login")
            public String login(@ModelAttribute UserDto dto, HttpSession session) {
                User user = userService.findByUsername(dto.getUsername());
            
                // 비밀번호 검증
                if (!bCryptPasswordEncoder.matches(dto.getPassword(), user.getPassword())) {
                    return "redirect:/login?error";  // 실패 → 다시 로그인
                }
            
                // ✅ 로그인 성공 → 세션에 사용자 정보 저장
                session.setAttribute("loginUser", user);
                return "redirect:/board/list";
            }
② 인증이 필요한 페이지 — 세션 확인
@GetMapping("/board/write")
            public String writeForm(HttpSession session, Model model) {
            
                // 세션에서 꺼내기
                User loginUser = (User) session.getAttribute("loginUser");
            
                // ❌ null이면 로그인 안 한 것 → 로그인 페이지로
                if (loginUser == null) {
                    return "redirect:/user/login";
                }
            
                // ✅ 로그인 상태 → 정상 진행
                model.addAttribute("user", loginUser);
                return "board/write";
            }
③ 로그아웃 — 세션 무효화
@GetMapping("/logout")
            public String logout(HttpSession session) {
                session.invalidate();  // 세션 전체 삭제 (서버 메모리에서 제거)
                return "redirect:/";
            }
🗂 서버 메모리 속 세션 구조
서버 메모리 (세션 저장소)
            ┌─────────────────────────────────────────┐
            │  세션 ID: "ABC123XYZ"                   │
            │  ┌───────────────────────────────────┐  │
            │  │ "loginUser"User{ id=1,         │  │
            │  │               name="홍길동",      │  │
            │  │               role="USER" }       │  │
            │  │ 만료시간: 30분 후                 │  │
            │  └───────────────────────────────────┘  │
            │                                         │
            │  세션 ID: "XYZ789ABC"  ← 다른 사용자   │
            │  ┌───────────────────────────────────┐  │
            │  │ "loginUser"User{ id=2, ... }   │  │
            │  └───────────────────────────────────┘  │
            └─────────────────────────────────────────┘
📌 주요 메서드 정리
setAttribute(key, value)
세션에 데이터 저장. 로그인 시 사용자 정보 저장에 사용
getAttribute(key)
세션에서 데이터 꺼내기. Object 반환이라 형변환 필요
invalidate()
세션 전체 삭제. 로그아웃 시 반드시 호출
setMaxInactiveInterval(sec)
세션 유지 시간 설정. 기본값 1800초(30분)
✅ 장점

구현이 단순하다
서버가 직접 관리 → 즉시 무효화 가능
민감한 정보가 서버에만 존재
Spring에서 파라미터로 바로 주입

❌ 한계 (→ JWT 전환 이유)

모든 Controller에 인증 코드 반복
서버 메모리 사용 (사용자 많을수록 ↑)
서버 여러 대일 때 세션 공유 불가
모바일/앱 환경에서 쿠키 불편

⚡ 1차 → 2차 전환 포인트
1차 프로젝트에서는 HttpSession으로 인증을 처리하지만,
2차(React + REST API)에서는 서버가 Stateless해야 하기 때문에 JWT 토큰 방식으로 전환한다.
세션은 서버가 상태를 기억해야 하지만, JWT는 토큰 자체에 정보를 담아 서버는 아무것도 기억하지 않는다.
반복되는 인증 체크 코드
Repeated Auth Check → Interceptor / ArgumentResolver
Spring
문제: 로그인이 필요한 페이지마다 아래 코드가 똑같이 반복됨. 이걸 InterceptorArgumentResolver로 한 곳에서 처리할 수 있어요.
😫 문제 — 컨트롤러마다 이게 반복됨
@GetMapping("/dashboard")
        public String dashboard(HttpSession session, Model model) {
            // ⬇️ 이 부분이 모든 Controller에 반복됨
            User loginUser = (User) session.getAttribute("loginUser");
            if (loginUser == null) {
                return "redirect:/login";
            }
            model.addAttribute("loginUser", loginUser);
        
            // 실제 비즈니스 로직
            model.addAttribute("data", dashboardService.getData());
            return "dashboard";
        }
        
        @GetMapping("/mypage")
        public String mypage(HttpSession session, Model model) {
            // ⬇️ 똑같은 코드 또 등장
            User loginUser = (User) session.getAttribute("loginUser");
            if (loginUser == null) {
                return "redirect:/login";
            }
            model.addAttribute("loginUser", loginUser);
            ...
        }
⚠️ 문제점
🔁
중복
세션 꺼내는 코드가 컨트롤러마다 복붙
💣
실수 위험
개발자가 깜빡하고 빠뜨리면 인증 없이 접근 가능
🔧
유지보수
로직 바꾸려면 모든 컨트롤러를 다 수정해야 함
✅ 해결책 — 역할 분리
Interceptor
세션에 loginUser가 없으면
redirect:/login

인증 체크를 한 곳에서 처리
ArgumentResolver
세션에서 loginUser
파라미터로 자동 주입

반복 코드를 자동화
📊 Before / After
❌ Before — 반복 코드 있는 컨트롤러
@GetMapping("/dashboard")
        public String dashboard(HttpSession session, Model model) {
            User loginUser = (User) session.getAttribute("loginUser");
            if (loginUser == null) return "redirect:/login";      // 인증 체크
            model.addAttribute("loginUser", loginUser);           // 모델 등록
        
            model.addAttribute("data", dashboardService.getData());
            return "dashboard";
        }
✅ After — Interceptor + ArgumentResolver 적용 후
@GetMapping("/dashboard")
        public String dashboard(@LoginUser User loginUser, Model model) {
            // 인증 체크 → Interceptor가 이미 처리
            // loginUser 주입 → ArgumentResolver가 자동으로 넣어줌
        
            // 실제 비즈니스 로직만 남음
            model.addAttribute("data", dashboardService.getData());
            return "dashboard";
        }
🎯 쉬운 비유
Interceptor = 건물 입구 보안요원 → 출입증 없으면 입장 자체를 막음
ArgumentResolver = 안내 데스크 → 입장한 사람에게 "당신 정보"를 자동으로 건네줌
컨트롤러는 이미 검증된 사람만 들어오고, 정보도 이미 손에 들려있는 상태
🔍 Interceptor 구현
@Component
        public class LoginCheckInterceptor implements HandlerInterceptor {
        
            @Override
            public boolean preHandle(HttpServletRequest request,
                                     HttpServletResponse response,
                                     Object handler) throws Exception {
        
                HttpSession session = request.getSession(false);
        
                if (session == null || session.getAttribute("loginUser") == null) {
                    response.sendRedirect("/login");
                    return false;  // false = 요청 진행 중단
                }
                return true;  // true = 다음 단계로 진행
            }
        }
Interceptor 등록 (WebConfig)
@Configuration
        public class WebConfig implements WebMvcConfigurer {
        
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                registry.addInterceptor(new LoginCheckInterceptor())
                        .addPathPatterns("/**")          // 모든 경로에 적용
                        .excludePathPatterns(            // 이 경로는 제외
                            "/", "/login", "/signup",
                            "/css/**", "/js/**", "/images/**"
                        );
            }
        }
🔍 ArgumentResolver 구현
// 1. 커스텀 어노테이션 만들기
        @Target(ElementType.PARAMETER)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface LoginUser {}
        
        // 2. ArgumentResolver 구현
        @Component
        public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
        
            @Override
            public boolean supportsParameter(MethodParameter parameter) {
                // @LoginUser 어노테이션이 붙은 파라미터에만 적용
                return parameter.hasParameterAnnotation(LoginUser.class)
                    && parameter.getParameterType().equals(User.class);
            }
        
            @Override
            public Object resolveArgument(MethodParameter parameter,
                                          ModelAndViewContainer mavContainer,
                                          NativeWebRequest webRequest,
                                          WebDataBinderFactory binderFactory) {
                HttpSession session = ((HttpServletRequest) webRequest
                    .getNativeRequest()).getSession(false);
                return session != null ? session.getAttribute("loginUser") : null;
            }
        }
ArgumentResolver 등록 (WebConfig에 추가)
@Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            resolvers.add(new LoginUserArgumentResolver());
        }
Interceptor만 써도 될 때

단순히 로그인 여부만 체크하면 되는 경우
컨트롤러에서 loginUser 객체가 필요 없을 때
model.addAttribute를 수동으로 해도 괜찮을 때

둘 다 쓸 때 (추천)

컨트롤러에서 loginUser를 자주 쓸 때
@LoginUser User loginUser 파라미터로 깔끔하게 받고 싶을 때
반복 코드를 완전히 없애고 싶을 때

SSR
Server Side Rendering
웹 기초
서버가 HTML을 완성해서 브라우저에 보내주는 방식. 브라우저는 받은 HTML을 그냥 보여주기만 한다. 1차 프로젝트에서 Thymeleaf가 이 역할을 한다.

비유하자면 식당에서 완성된 요리를 받아서 먹기만 하는 것과 같다.
SSR (1차)

서버가 HTML 완성 → 브라우저는 보여주기만 함
Thymeleaf

CSR (2차)

서버는 JSON만 전달 → 브라우저가 직접 화면 조립
React

SSR vs CSR
Server Side vs Client Side Rendering
웹 기초
서버가 HTML을 완성해서 브라우저에 보내주는 방식. 브라우저는 받은 HTML을 그냥 보여주기만 한다.
SSR — 1차 (Thymeleaf)
브라우저: "게시판 목록 줘" 서버: HTML 완성해서 통째로 전달 "<html>... <tr>글제목1</tr> <tr>글제목2</tr> ...</html>" 브라우저: 받은 HTML 보여주기만 함
CSR — 2차 (React)
브라우저: "게시판 목록 줘" 서버: 데이터만 전달 "[{title:'글제목1'}, {title:'글제목2'}]" 브라우저: 데이터로 HTML 직접 조립해서 화면 그림
🍽️ SSR = 식당에서 완성된 요리를 받아서 먹기만 함
🥘 CSR = 재료를 받아서 내가 직접 조리해서 먹음
1차에서 Thymeleaf가 바로 SSR 역할을 한다. board/list.html 템플릿에 데이터를 끼워넣어서 완성된 HTML을 만들어 브라우저로 보내준다.
CSR
Client Side Rendering
웹 기초
서버는 데이터(JSON)만 보내고, 브라우저가 직접 HTML을 조립해서 화면을 그리는 방식. 2차 프로젝트에서 React가 이 역할을 한다.

비유하자면 재료를 받아서 내가 직접 조리해서 먹는 것과 같다.
// 서버는 이런 JSON만 내려줌 [ { "id": 1, "title": "첫 번째 글", "author": "홍길동" }, { "id": 2, "title": "두 번째 글", "author": "김철수" } ] // React가 이걸 받아서 화면으로 만듦
JWT
JSON Web Token
인증 / 보안
로그인 정보를 서버가 아닌 토큰 자체에 담아서 클라이언트가 보관하는 인증 방식. 서버는 토큰을 발급만 하고 저장하지 않는다(Stateless). 이후 요청마다 클라이언트가 토큰을 헤더에 담아 보내면 서버가 검증한다.
Session (1차)

서버 메모리에 저장
서버가 상태 관리
서버 여러 대면 공유 문제

JWT (2차)

클라이언트가 토큰 보관
서버는 Stateless
서버 여러 대여도 문제 없음

JPA
Java Persistence API
DB / JPA
SQL을 직접 작성하지 않고 Java 객체로 DB를 다룰 수 있게 해주는 기술. @Entity 어노테이션이 붙은 클래스를 보고 자동으로 테이블을 만들어준다. JpaRepository를 상속하면 기본 CRUD 메서드를 자동으로 제공한다.
// JpaRepository 상속만 하면 save(), findById(), delete() 등 자동 제공 public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByUsername(String username); // 이것도 자동 구현 }
ddl-auto
spring.jpa.hibernate.ddl-auto
DB / JPA
서버 시작 시 JPA가 테이블을 어떻게 처리할지 결정하는 설정. application.properties에 지정한다.
spring.jpa.hibernate.ddl-auto=update
주요 옵션:
create 서버 시작마다 테이블 새로 생성 (기존 데이터 삭제됨)
update 테이블 없으면 생성, 있으면 변경분만 반영 (데이터 유지)
validate Entity와 테이블 구조가 맞는지 확인만, 변경 안 함
none 아무것도 안 함 (운영 환경에서 사용)
BCrypt
BCrypt Password Encoder
인증 / 보안
비밀번호를 단방향 해시로 암호화하는 알고리즘. 암호화된 값으로 원본을 역추적할 수 없다. 같은 비밀번호를 암호화해도 매번 다른 값이 나오기 때문에 DB가 털려도 원본 비밀번호를 알 수 없다.
// 회원가입 시 암호화해서 저장 user.setPassword(bCryptPasswordEncoder.encode(rawPassword)); // 로그인 시 입력값과 DB 저장값 비교 bCryptPasswordEncoder.matches(rawPassword, encodedPassword); // true/false
Thymeleaf
Template Engine
Spring
Spring에서 사용하는 서버사이드 템플릿 엔진. HTML 파일 안에 th: 속성을 사용해서 서버 데이터를 HTML에 끼워넣는다. Controller가 Model에 담아 보낸 데이터를 화면에 출력한다.
<!-- Controller에서 model.addAttribute("boardList", list) 로 전달 --> <tr th:each="board : ${boardList.content}"> <td th:text="${board.title}"></td> <td th:text="${board.user.nickname}"></td> </tr>
DTO
Data Transfer Object
Java
데이터를 전달하는 전용 객체. Entity(DB 테이블과 매핑된 클래스)를 직접 외부에 노출하지 않고, 필요한 데이터만 담아서 주고받기 위해 사용한다. 폼 데이터를 받을 때 @ModelAttribute로 자동 바인딩된다.
// 폼에서 username, password 입력 → UserDto에 자동으로 담김 @PostMapping("/login") public String login(@ModelAttribute UserDto userDto, HttpSession session) { // userDto.getUsername(), userDto.getPassword() 로 사용 }
Entity
JPA Entity Class
DB / JPA
DB 테이블과 1:1로 매핑되는 Java 클래스. @Entity 어노테이션을 붙이면 JPA가 이 클래스를 기반으로 테이블을 자동 생성한다. 클래스의 필드 = 테이블의 컬럼.
@Entity @Table(name = "users") // DB 테이블 이름 지정 public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // → id 컬럼 (PK, AUTO_INCREMENT) @Column(unique = true, nullable = false) private String username; // → username 컬럼 (UNIQUE, NOT NULL) }
@Transactional
Transaction / Dirty Checking
Spring
메서드를 트랜잭션 안에서 실행하게 해주는 어노테이션. 이 어노테이션이 붙은 메서드 안에서 Entity 필드를 변경하면, 메서드가 끝날 때 JPA가 자동으로 UPDATE 쿼리를 날린다. 이것을 더티체킹(Dirty Checking)이라고 한다.
@Transactional public void update(Long id, BoardDto boardDto) { Board board = boardRepository.findById(id).orElseThrow(); board.setTitle(boardDto.getTitle()); // 값만 바꾸면 board.setContent(boardDto.getContent()); // boardRepository.save() 안 해도 자동 UPDATE 됨! }
반복되는 인증 체크 코드
Repeated Auth Check → Interceptor / ArgumentResolver
Spring
문제: 로그인이 필요한 페이지마다 아래 코드가 똑같이 반복됨. 이걸 InterceptorArgumentResolver로 한 곳에서 처리할 수 있어요.
😫 문제 — 컨트롤러마다 이게 반복됨
@GetMapping("/dashboard")
        public String dashboard(HttpSession session, Model model) {
            // ⬇️ 이 부분이 모든 Controller에 반복됨
            User loginUser = (User) session.getAttribute("loginUser");
            if (loginUser == null) {
                return "redirect:/login";
            }
            model.addAttribute("loginUser", loginUser);
        
            // 실제 비즈니스 로직
            model.addAttribute("data", dashboardService.getData());
            return "dashboard";
        }
        
        @GetMapping("/mypage")
        public String mypage(HttpSession session, Model model) {
            // ⬇️ 똑같은 코드 또 등장
            User loginUser = (User) session.getAttribute("loginUser");
            if (loginUser == null) {
                return "redirect:/login";
            }
            model.addAttribute("loginUser", loginUser);
            ...
        }
⚠️ 문제점
🔁
중복
세션 꺼내는 코드가 컨트롤러마다 복붙
💣
실수 위험
개발자가 깜빡하고 빠뜨리면 인증 없이 접근 가능
🔧
유지보수
로직 바꾸려면 모든 컨트롤러를 다 수정해야 함
✅ 해결책 — 역할 분리
Interceptor
세션에 loginUser가 없으면
redirect:/login

인증 체크를 한 곳에서 처리
ArgumentResolver
세션에서 loginUser
파라미터로 자동 주입

반복 코드를 자동화
📊 Before / After
❌ Before — 반복 코드 있는 컨트롤러
@GetMapping("/dashboard")
        public String dashboard(HttpSession session, Model model) {
            User loginUser = (User) session.getAttribute("loginUser");
            if (loginUser == null) return "redirect:/login";      // 인증 체크
            model.addAttribute("loginUser", loginUser);           // 모델 등록
        
            model.addAttribute("data", dashboardService.getData());
            return "dashboard";
        }
✅ After — Interceptor + ArgumentResolver 적용 후
@GetMapping("/dashboard")
        public String dashboard(@LoginUser User loginUser, Model model) {
            // 인증 체크 → Interceptor가 이미 처리
            // loginUser 주입 → ArgumentResolver가 자동으로 넣어줌
        
            // 실제 비즈니스 로직만 남음
            model.addAttribute("data", dashboardService.getData());
            return "dashboard";
        }
🎯 쉬운 비유
Interceptor = 건물 입구 보안요원 → 출입증 없으면 입장 자체를 막음
ArgumentResolver = 안내 데스크 → 입장한 사람에게 "당신 정보"를 자동으로 건네줌
컨트롤러는 이미 검증된 사람만 들어오고, 정보도 이미 손에 들려있는 상태
🔍 Interceptor 구현
@Component
        public class LoginCheckInterceptor implements HandlerInterceptor {
        
            @Override
            public boolean preHandle(HttpServletRequest request,
                                     HttpServletResponse response,
                                     Object handler) throws Exception {
        
                HttpSession session = request.getSession(false);
        
                if (session == null || session.getAttribute("loginUser") == null) {
                    response.sendRedirect("/login");
                    return false;  // false = 요청 진행 중단
                }
                return true;  // true = 다음 단계로 진행
            }
        }
Interceptor 등록 (WebConfig)
@Configuration
        public class WebConfig implements WebMvcConfigurer {
        
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                registry.addInterceptor(new LoginCheckInterceptor())
                        .addPathPatterns("/**")          // 모든 경로에 적용
                        .excludePathPatterns(            // 이 경로는 제외
                            "/", "/login", "/signup",
                            "/css/**", "/js/**", "/images/**"
                        );
            }
        }
🔍 ArgumentResolver 구현
// 1. 커스텀 어노테이션 만들기
        @Target(ElementType.PARAMETER)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface LoginUser {}
        
        // 2. ArgumentResolver 구현
        @Component
        public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
        
            @Override
            public boolean supportsParameter(MethodParameter parameter) {
                // @LoginUser 어노테이션이 붙은 파라미터에만 적용
                return parameter.hasParameterAnnotation(LoginUser.class)
                    && parameter.getParameterType().equals(User.class);
            }
        
            @Override
            public Object resolveArgument(MethodParameter parameter,
                                          ModelAndViewContainer mavContainer,
                                          NativeWebRequest webRequest,
                                          WebDataBinderFactory binderFactory) {
                HttpSession session = ((HttpServletRequest) webRequest
                    .getNativeRequest()).getSession(false);
                return session != null ? session.getAttribute("loginUser") : null;
            }
        }
ArgumentResolver 등록 (WebConfig에 추가)
@Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            resolvers.add(new LoginUserArgumentResolver());
        }
Interceptor만 써도 될 때

단순히 로그인 여부만 체크하면 되는 경우
컨트롤러에서 loginUser 객체가 필요 없을 때
model.addAttribute를 수동으로 해도 괜찮을 때

둘 다 쓸 때 (추천)

컨트롤러에서 loginUser를 자주 쓸 때
@LoginUser User loginUser 파라미터로 깔끔하게 받고 싶을 때
반복 코드를 완전히 없애고 싶을 때

🔍 검색 결과가 없어요. 다른 키워드로 찾아보세요!
✅ 등록 완료

아래 버튼으로 현재 상태가 반영된 glossary.html을 다운로드하세요.
기존 파일을 덮어씌우면 영구 저장 완료!

카드 HTML 수정
💡 HTML 소스를 직접 수정하고 저장하기를 누르면 카드가 바뀌어요.
📎 이 페이지 사용 가이드 — 다른 학습 페이지에서 링크 연결하는 방법
다른 학습 페이지 → Glossary 링크 연결

공부 중에 모르는 용어가 나오면 바로 이 페이지로 연결해두세요. 클릭하면 해당 카드로 이동하고 자동으로 펼쳐져요.

핵심 공식
glossary.html#카드ID
URL 뒤에 # + ID 만 붙이면 끝
사용 방법
1
아래 카드 ID 목록에서 연결할 용어의 ID를 확인
2
공부 페이지에서 텍스트를 <a href="glossary.html#ID" target="_blank">용어</a> 로 감싸기
3
파일 위치에 따라 경로 조정 — 같은 폴더: glossary.html#id / 상위 폴더: ../glossary.html#id
4
클릭하면 이 페이지로 이동 + 해당 카드 자동 펼침 ✅ (target="_blank" 쓰면 새 탭으로 열려서 공부 페이지가 안 닫혀요)
현재 카드 ID 목록
SSRssr
SSR vs CSRssr-detail
CSRcsr
JWTjwt
JPAjpa
ddl-autoddl-auto
BCryptbcrypt
Thymeleafthymeleaf
DTOdto
Entityentity
HttpSessionhttpsession
@Transactionaltransactional
💡 새로 등록한 카드는 소스에서 id="..." 부분을 직접 확인하세요. HTML 직접 입력 시 ID를 bcrypt, spring-mvc 처럼 기억하기 쉽게 지정해두면 편해요.
실제 코드 예시
① 기본
<a href="glossary.html#jwt" target="_blank">JWT</a>
② CSS 클래스로 통일 (공부 페이지에 한 번만 추가)
/* 공부 페이지 CSS */
.gl {
  color: var(--accent2);
  text-decoration: none;
  border-bottom: 1px dashed currentColor;
  font-weight: 500;
}
.gl:hover { opacity: 0.7; }

/* 사용 */
<a href="glossary.html#jwt" class="gl" target="_blank">JWT</a>
③ 다른 폴더에 있을 때
<!-- 현재: study/spring/session.html → glossary: glossary.html -->
<a href="../../glossary.html#httpsession" target="_blank">HttpSession</a>

📝 개발 공부 방법에 대한 메모

이 glossary.html 하나에 들어간 기술들:

HTML/CSS 레이아웃 & 다크/라이트 테마
JavaScript DOM 조작 (카드 생성·삭제·순서 변경)
HTML5 드래그앤드롭 API
localStorage 데이터 관리
File System API (다운로드)
마크다운 파서 직접 구현
이벤트 버블링/캡처링 제어

이걸 처음부터 혼자 구현하려면 솔직히 꽤 걸려요. 각 기술마다 따로 공부해야 하고, 조합하는 것도 또 다른 문제거든요.

근데 지금 하고 계신 방식이 사실 제일 효율적인 방법이에요.

개념 이해 (공부)
+
Claude로 구현 (도구 활용)
+
직접 써보면서 파악 (경험)

개발자도 다 외워서 짜는 게 아니라 필요한 걸 찾아서 조합하는 거거든요. "이런 기능이 필요해"를 명확하게 요구하고, 결과물을 보고 "이건 왜 이렇게 동작하지?"를 이해해가는 게 오히려 더 빠른 길이에요.


🎨 코드 색상이 들어오는 원리 — Prism.js

이 페이지의 코드 블록에 색상이 들어오는 건 Prism.js 라는 오픈소스 라이브러리 덕분이에요. CDN 링크 3줄만 추가하면 Java, HTML, SQL 등 300개 이상의 언어를 자동으로 인식해서 색을 입혀줘요.

동작 원리
1
<pre><code class="language-java">코드</code></pre> 형태로 코드 작성
2
Prism이 언어 문법 규칙대로 키워드·문자열·주석 등을 파싱
3
<span class="token keyword">public</span> 처럼 각 요소에 자동으로 span 태그 삽입
4
테마 CSS가 token 클래스마다 색상 적용 → 눈에 보이는 하이라이팅 완성
이 glossary에서의 적용 방식
카드 안의 pre.term-example 블록을 페이지 로드 시 자동으로 감지해서 하이라이팅을 적용해요. @·public·class 키워드가 있으면 Java, <태그> 구조면 HTML, SELECT·FROM이 있으면 SQL로 자동 판단해요. 그래서 카드 작성할 때 언어 클래스를 따로 지정하지 않아도 대부분 알아서 색이 들어와요.

단, 동적으로 추가된 카드(용어 추가 버튼으로 등록한 것)는 카드를 클릭해서 열 때 Prism.highlightAll()이 호출되면서 그때 색상이 적용돼요.
테마 변경 방법

테마를 바꾸고 싶으면 <head> 안의 CSS CDN 링크에서 테마 이름만 교체하면 전체가 바뀌어요.

<!-- 현재 적용 중 -->
<link rel="stylesheet" href=".../themes/prism-tomorrow.min.css">

<!-- 다른 테마로 교체 예시 -->
<link rel="stylesheet" href=".../themes/prism-okaidia.min.css">
<link rel="stylesheet" href=".../themes/prism-vsc-dark-plus.min.css">
<link rel="stylesheet" href=".../themes/prism-solarizedlight.min.css">