블로그
전체 2#카테고리
- 백엔드
#태그
- 인프런강의
- 백엔드
- 코틀린
- 나만의포폴사이트
2025. 03. 16.
0
백엔드 코틀린 개발하기 2주차 모르는 개념 정리
Domain 개발하기데이터 초기화@Component // 스프링 빈으로 등록어떤 클래스를 빈으로 등록할지 알려주는 어노테이션📌 @Component란?@Component는 Spring Framework에서 빈(Bean)으로 등록하기 위한 기본적인 애너테이션입니다.즉, Spring이 해당 클래스를 자동으로 스프링 컨테이너(IOC 컨테이너)에 등록하도록 해줍니다.🚀 @Component 사용 방법@Component class MyService { fun doSomething() { println("MyService is working!") } } @Component를 사용하면 MyService 클래스가 Spring Bean으로 등록됨.다른 곳에서 @Autowired 또는 생성자 주입을 통해 사용할 수 있음.@Service class SomeClass(private val myService: MyService) { fun execute() { myService.doSomething() } } SomeClass에서 MyService를 자동으로 주입받아 사용 가능 🎯 @Component vs 기타 빈 애너테이션Spring에서는 @Component 외에도 특정한 목적에 맞는 다른 애너테이션들이 있습니다.애너테이션 설명 @Component 가장 기본적인 빈 등록 애너테이션 @Service 비즈니스 로직을 처리하는 클래스에 사용 @Repository 데이터베이스 작업(DAO, Repository)에 사용 @Controller Spring MVC 컨트롤러(웹 요청 처리)에 사용💡 실제 동작은 동일하지만, 역할에 맞게 애너테이션을 사용하는 것이 가독성과 유지보수성을 높이는 데 도움이 됩니다.📌 @Component를 어디서 쓰면 좋을까?✅ 사용하면 좋은 경우특정한 역할이 없는 일반적인 Bean을 만들 때 (Utility Class, Helper, Config)다른 서비스, 레포지토리와는 다르게 특별한 역할이 없는 경우❌ 다른 애너테이션을 쓰는 것이 좋은 경우비즈니스 로직을 처리하는 경우 → @Service데이터 액세스 관련 클래스 → @Repository웹 요청을 처리하는 컨트롤러 → @Controller🔥 결론@Component는 Spring Bean을 등록하는 가장 기본적인 애너테이션.역할이 명확한 경우 @Service, @Repository, @Controller를 사용하는 것이 더 가독성이 좋음.주로 일반적인 유틸리티 클래스나 특정한 계층에 속하지 않는 클래스에서 사용됨. 🚀📌 @PostConstruct란?@PostConstruct는 Spring에서 빈(Bean)이 생성된 후 자동으로 실행되는 메서드를 지정하는 애너테이션입니다.즉, 스프링 컨테이너가 해당 빈을 생성하고 의존성 주입(DI)이 완료된 후 한 번 실행되는 초기화 메서드를 정의할 때 사용된다.🚀 @PostConstruct 기본 사용 예시@Component class MyService { @PostConstruct fun initializeData() { println("MyService 초기화 중...") // 초기 데이터 설정 로직 } } ✅ 실행 흐름:MyService가 스프링 컨테이너에 의해 빈(Bean)으로 생성됨.의존성 주입(DI)이 완료됨.@PostConstruct가 붙은 initializeData() 메서드가 자동 실행됨.💡 @PostConstruct의 주요 특징Spring Bean이 생성된 후, 단 한 번만 실행됨.의존성 주입이 완료된 후 실행되므로, 다른 빈을 사용할 수 있음.애플리케이션이 실행될 때 한 번만 초기화해야 하는 작업에 적합.🎯 @PostConstruct가 유용한 경우사용 예시 설명 초기 데이터 설정 DB에서 기본 데이터를 불러오거나, 캐시를 로딩할 때 외부 API 연동 애플리케이션 시작 시 특정 API와 통신해야 할 때 리소스 초기화 특정 설정 파일을 읽거나, 초기 환경을 세팅할 때📌 데이터 초기화 예시@Service class DataService(private val repository: MyRepository) { @PostConstruct fun initializeDatabase() { if (repository.count() == 0L) { repository.saveAll(listOf(MyEntity("데이터1"), MyEntity("데이터2"))) println("초기 데이터 삽입 완료") } } } ✅ 데이터베이스에 초기 데이터를 넣어야 할 때 유용함.⚠ 주의할 점❌ 비동기 작업 (@Async)과 함께 사용하면 안 됨@PostConstruct는 빈 초기화 이후 즉시 실행되므로 비동기 메서드와 충돌 가능.대안: ApplicationRunner 또는 CommandLineRunner를 활용.@Bean fun initRunner() = ApplicationRunner { println("애플리케이션 실행 후 초기화 코드 실행") } 🔥 결론@PostConstruct는 Spring Bean이 생성된 후 단 한 번 실행되는 초기화 메서드.초기 데이터 설정, API 호출, 설정값 초기화 등에 유용.비동기 작업과 함께 사용하면 예상치 못한 문제가 발생할 수 있음.→ 이럴 때는 ApplicationRunner를 고려하는 것이 좋음. 🚀📌 achievementRepository.saveAll(achievements) 데이터가 없는데도 되는 이유Spring Data JPA의 saveAll() 메서드는 비어있는 리스트(List)를 전달해도 오류 없이 실행되도록 설계되어 있다.즉, achievements 리스트가 비어있다면 아무런 데이터도 저장되지 않고, 그냥 정상적으로 메서드가 종료된다.🚀 saveAll() 동작 방식achievementRepository.saveAll(emptyList()) // 데이터가 없어도 실행됨 ✔ 내부적으로 실행되는 로직비어있는 리스트(emptyList())가 들어오면, 아무 동작 없이 바로 반환.데이터가 있으면 각각의 엔티티를 save() 메서드를 이용해 Batch Insert 또는 개별 저장 수행.트랜잭션이 있으면 한 번에 처리되고, 없으면 개별 실행됨.🎯 saveAll()이 실행될 때 두 가지 경우1⃣ 데이터가 있을 때val achievements = listOf( Achievement(name = "Level 1", score = 10), Achievement(name = "Level 2", score = 20) ) achievementRepository.saveAll(achievements) // DB에 저장됨 saveAll()은 리스트에 있는 엔티티들을 한꺼번에 저장함.2⃣ 데이터가 없을 때val emptyList = emptyList() achievementRepository.saveAll(emptyList) // 아무 일도 일어나지 않음 saveAll()은 빈 리스트가 들어오면 아무런 작업도 하지 않고 그냥 종료됨.SQL 쿼리가 실행되지 않으며, 오류도 발생하지 않음.🔥 결론✅ saveAll(emptyList())는 아무런 데이터도 저장하지 않고 그냥 종료되도록 설계됨.✅ 데이터가 없을 경우에도 오류 없이 정상 동작함.✅ Spring Data JPA는 이런 경우를 고려하여 예외를 던지지 않음.📌 즉, 데이터가 없어도 saveAll()이 실행되지만, 실제로 DB에는 아무런 변경이 일어나지 않는 것! 🚀 Kotlin에서 fun의 의미Kotlin에서 fun 키워드는 함수를 정의할 때 사용되는 키워드Java의 public List findAllByIsActive(boolean isActive)와 같은 역할을 한다.🚀 fun findAllByIsActive(isActive: Boolean): List// select * from skill where is_active = :isActive fun findAllByIsActive(isActive: Boolean): List ✅ 설명findAllByIsActive(isActive: Boolean):isActive 값이 true 또는 false인 데이터를 조회하는 메서드.SQL 변환: SELECT * FROM skill WHERE is_active = :isActive반환 타입: List → 여러 개의 Skill 객체 리스트를 반환.🔹 사용 예시val activeSkills: List = skillRepository.findAllByIsActive(true) println(activeSkills) // 활성화된 스킬 목록 출력 🚀 fun findByNameIgnoreCaseAndType(name: String, type: SkillType): Optional// select * from skill where lower(name) = lower(:name) and type = :type fun findByNameIgnoreCaseAndType(name: String, type: SkillType): Optional ✅ 설명findByNameIgnoreCaseAndType(name: String, type: SkillType):이름(name) 대소문자 구분 없이 검색하고,타입(type)이 일치하는 데이터를 조회.SQL 변환: SELECT * FROM skill WHERE lower(name) = lower(:name) AND type = :type반환 타입: Optional → 조회된 값이 있을 수도 있고, 없을 수도 있음.🔹 사용 예시val skill: Optional = skillRepository.findByNameIgnoreCaseAndType("java", SkillType.PROGRAMMING) if (skill.isPresent) { println("Skill found: ${skill.get()}") } else { println("Skill not found") } 🔥 결론Kotlin에서 fun은 함수 선언 키워드.findAllByIsActive(isActive: Boolean): 활성화된(is_active) 데이터를 리스트로 반환.findByNameIgnoreCaseAndType(name, type): 대소문자 구분 없이 특정 이름과 타입을 가진 데이터를 조회.Spring Data JPA는 메서드 이름을 기반으로 자동으로 SQL을 생성하여 실행함. 🚀 회고사실 왕복 3시간 거리 회사를 다니고 지금 커피챗, 친구들 만나기, 스터디 2개까지 해서 솔직히 버거워서 밀렸음 그래도 일단 과제를 내고자 하는 의지로 내는 중...
2025. 03. 09.
0
인프런 워밍업 클럽 3기 백엔드 스터디
프로젝트에 필요한 상수는 어떻게 관리할까?자바 코드에 직접 넣기같은 값을 여러 클래스에서 사용한다면 값이 수정될 때마다 모든 클래스에서 값을 찾아 바꿔줘야 할까?개발 서버와 운영 서버에서 다른 상수 값을 쓰고 싶다면?스프링 Profile과 application.yml스프링은 애플리케이션의 Profile을 정의할 수 있는 기능을 제공한다.실행 시 환경 변수로 정의 가능한다.따로 정의되지 않고 실행되면 default로 설정된다.No active profile set, falling back to 1 default profile: "default" application-{Profile} 형식으로 yml 또는 properties 파일을 생성하면 Profile에 따라 상수 값 설정 가능하다. 소스 코드상에서는 Enviromnent를 이용해 키로 값을 가져올 수 있다.환경별로 세팅해주기~git status :No commits yet, Untracked files 추적하지 않는 파일들도 보여줌https://www.toptal.com/developers/gitignore/ 사이트에 가면 gitgnore이 만들어짐 thymeleafhtml 엔진에 데이터를 넣어 동적으로 움직이게 해줌데이터베이스에서 가져온 데이터를 model에 넣어주고 어떤 템플릿을 쓸 건지 해서 템플릿 + 데이터가 된 html 최종을 만들어줌브라우저에서 html에 그려줌Validation어노테이션을 클라이언트에서 들어오는 값 검증문제가 생긴다면setting > grale > gradle jvm을 다시 다운 받자 타입datetime : 날짜와 시간을 같이 가지고 있는 자료형 컬럼명변경 사항을 추적하기 위함created_date_time : 메타 데이터updated_date_time : 메타 데이터 name을 기반으로 github 링크 라던지, 부트스트랩 아이콘를 보여줌 experience : experience_detail1: N으로 묶인 테이블project_skill : project skill은 다 대 다 관계로 매핑해주는 테이블http_interface : 사용자가 요청하는 테이블 확인 할 수 있음 domainconfiguration : 암호화constant : 도메인 패키지에서 사용하는 함수들entity : 데이터 베이스 테이블에 상응하는 객체repository : entity 별로 C/R/U/D 작업하는 것 들어감 presentation : domin이랑 합치면 하나의 독립된 프로젝트로 볼 수 있음controllerdto : 화면에서 필요하는 데이터들을 따로 묶음interceptor : 컨트롤러까지 요청 들어가기 전 모든 컨트롤러에 대해 공통 처리를 해주는 class, 각 화면을 조회할 때 요청에 대한 정보를 따로 db에 저장하는 기능 개발repository : 데이터 베이스와 직접 상호 작용 x 도메인 repository 레이어에서 필요로 하는 데이터를 중간에서 매핑해주는 역할service adminadvice : 예외 처리data : api 응답 공통 포맷, 입력 폼 정보, 테이블 정보를 담는 데이터 클래스exception : admin 페이지의 예외interceptor : 화면에서 보여지는 사이드바를 초기화하는 interceptorsecurity : 로그인 관련된 것들context : 만들 테이블, 확장성 있게achievementcontrollerserviceformdashboardexperienceintroductionlinkprojectskill [이론] JPA란JPA는 ORM(Obeject Relational Mapping) 기술이다.건물을 짓는 설계도로 건물을 올리게 되면 3D로 나오게 되는 것을 모델링이라고 한다.모델링 한다는 것은 추상화적인 개념을 현실세계에 구체화하는 것을 말한다.자바가 가지고 있는 데이터 타입과 데이터베이스가 가지고 있는 데이터 타입은 다르다. class를 통해서 데이터베이스에 있는 테이블을 모델링해야 한다.원래대로라면, 첫 번째로는 DB 테이블을 만들고 2번째로 자바세상에 모델링한다. ⇒ TRM(Table Relational Mapping)class Team{ int id; String name; String year; } 하지만, JPA경우 첫 번째로 자바에서 코드를 작성한 것인 클래스를 만들어서 두 번째로 데이터베이스 테이블을 자동으로 생성 수 있다. 이때 필요한 것이 인터페이스이다.ORM이란?Object Relational Mapping의 약어로, 객체 관계 매핑이라고 번역됨개발자가 직접 쿼리를 작성하는 과정이 생략되어 생산성이 향상됨데이터를 객체지향적인 관점에서 접근할 수 있게 한다.구체적인 DBMS에 대한 의존성이 줄어듦.단점JPA로 데이터베이스를 정확히 다루려면 충분한 학습이 필요성능이 악화되거나, 의도하지 않은 데이터베이스 수정이 발생할 수 있다.ORM만으로 원하는 모든 기능을 구현하기엔 한계가 있다.불가피하게 구체적인 DBMS에 의존하는 네이티브 쿼리를 작성해야 할 수 있다.JPA를 사용하기 위해 꼭 알아야하는 트랜잭션정의여러 개의 데이터베이스 작업을 하나로 묶어주는 논리적인 단위다 같이 안되거나 다 같이 되거나 => 트랜잭션영속성 컨텍스트영속성이란데이터 ⇒ 영구적 저장(파일시스템 X, DB O)컨텍스트란?(Context)Context란 대상의 모든 정보를 가지고 있는 것개발에서 컨텍스트 개념은 조금 애매하다. 이렇게 이해하면 좋다고 한다. 길동이가 영숙이에게 난 영숙이 너의 모든 컨텍스트를 가지고 있어 = 영숙이의 모든 것을 알고 있다.Context를 넘겨준다. = 보고가 길동이에게 영숙이의 Context(모든 정보)를 넘겨영속성 컨텍스트란?JPA에서 엔티티를 관리하는 임시 메모리, 버퍼와 같은 개념자바가 데이터베이스를 저장해야 하는 모든 데이터를 아는 것이 영속성 컨텍스트자바에 있는 동물 데이터를 영속성 컨텍스트에 넣고 나서 DB에도 저장 후 영속성 컨텍스트에서 삭제를 하면 동기화가 되어 있기에 DB에서도 삭제가 된다.일단 자바는 1. 영속성 컨텍스트에 먼저 요청을 한다. 2. 영속성 컨텍스트에 데이터가 없기에 DB에 요청 한다. 3. DB에서 가져와 자바 object로 영속성 컨텍스트에 저장한다. 4. 자바에 과일데이터를 준다.일단 자바는 1. 영속성 컨텍스트에 먼저 요청을 한다. 2. 영속성 컨텍스트에 데이터가 없기에 DB에 요청 한다. 3. DB에서 가져와 자바 object로 영속성 컨텍스트에 저장한다. 4. 자바에 과일데이터를 준다.데이터 베이스에 있는 데이터를 select해서 가져오고 update하는 등 일련의 모든 정보를 영속성 컨텍스트를 통해 확인 할 수 있다. 자바가 데이터베이스에 저장해야 하는 모든 메타 데이터 정보들을 영속성 컨텍스트가 가지고 있다. java에서 과일 데이터를 딸기로 바꾸면..영속성 컨텍스트의 과일데이터도 딸기로 바뀌고데이터베이스에 commit해서 밀어넣으면 원래 셋은 같은 데이터 인데 3번은 사과로 되어 있어 딸기로 바꾸기위해 update가 자동으로 일어난다.데이터베이스 정의(위키피디아) 여러 사람이 공유하여 사용할 목적으로 체계화해 통합, 관리하는 데이터의 집합이다.이론적으로 데이터의 집합을 의미한다. 엑셀에 표로 정리된 학생부, 입출금 내역 등도 데이터베이스라고 할 수 있다. 하지만 일반적으로웹 개발에서 데이터베이스라고 하면 DBMS를 의미하는 경우가 많다.관계를 꼼꼼히 따져보는 것이 좋다 특히 1:1, 1:N, N:M 등의 관계가 중요하다대표적인 관계형 DBMS(RDBMS)Oracle Database오라클 사에서 만든 데이터베이스입니다. 막강한 성능과 안정성이 장점이지만 유료라는 단점이 있다.MySQL오픈소스로 시작했지만 현재는 오라클 소유PostgreSQL꾸준히 점유율이 올라가는 오픈소스 데이터베이스비관계형 데이터베이스? NoSQL?관계형 데이터베이스를 제외한 모든 종류의 데이터베이스를 비관계형 데이터베이스라고 부른다. 키-값형, 문서형 등이 있다.대표적인 비관계형 DBMSMongoDB문서형 데이터베이스데이터를 비정형적으로 저장할 수 있다.RedisKey-Value 스토어 데이터베이스캐시 용도로 주로 사용 [이론] HTTP와 REST API복잡한 비즈니스에서는 모든 기능을 REST를 준수하며 개발하기가 매우 어렵습니다. 가능한 REST 규칙을 따르되, 불가피한 부분에서 개발자가 참고할 수 있는 팀의 컨벤션을 만들어두는 것이 좋다.구글 API 디자인 가이드https://cloud.google.com/apis/design?hl=ko 클라이언트에서 서버로 데이터를 전달하는 방법Query Paratmeter쿼리 스트링이라고도 부른다. 스프링에서는 컨트롤러에 @RequestParam 어노테이션으로 간편하게 가져올 수 있다.HTTP Request Body스프링에서는 컨트롤러에 @RequestBody 어노테이션으로 간편하게 가져올 수 있다.Path Variable경로 변수라고도 한다. URL에 접근하고자 하는 자원의 식별자를 표현한다. 스프링에서는 @PathVariable 어노테이션으로 간편하게 가져올 수 있다get으로 모든 로드맵 가져옴 : https://www.inflearn.com/roadmaps뒤에 숫자를 붙여 id를 가져옴으로 특정 페이지를 가지고 옴https://www.inflearn.com/roadmaps?terms=5&page=1에서 terms=5&page=1가 쿼리 파라미터임[이론] 웹 프레임워크와 Spring웹 프레임워크프레임워크를 사용한다면 포크레인, 덤프트럭을 사용한다고 할 수 있습니다.프레임워크도 시간이 지남에 따라 발전하며 점점 사용성이 좋아진다.프레임워크? 라이브러리?프레임워크와 라이브러리를 구분하는 키는 ‘제어의 주도권’비유하면 프레임워크는 DIY 가구 키트라고 할 수 있다. 라이브러리는 가구를 만들기 위한 망치와 같은 공구, 또는 그런 공구들을 모아둔 공구 상자라 할 수 있습니다. 사용자가 주도권을 가지고 원하는 것을 만들 수 있다.물론 가구 키트를 만들며 망치를 사용할 수 있듯이, 프레임워크 안에서도 라이브러리를 사용할 수 있다.Spring FrameworkJava 기반의 웹 프레임워크이다.MVC 패턴(Model-View-Controller)MVC 패턴은 소프트웨어 아키텍처 디자인 패턴요청의 처리(비즈니스 로직), 데이터, 화면 간의 결합도를 낮추면서 유지보수를 용이Model: 데이터를 담는다. Controller는 데이터를 넣고, Views는 데이터를 꺼내올 수 있다.View: 사용자에게 보여지는 화면을 담당한다. Model에서 데이터를 꺼내올 수 있다.Controller: 요청을 받아 작업을 수행한다. 작업의 결과 데이터를 Model에 넣을 수 있다.레이어드 아키텍처(Controller-Service-Repository)Presentation(Controller): 클라이언트가 요청할 수 있는 인터페이스를 정의한다. 전달받은 데이터를 검증하고, Service의 메소드(처리 로직)을 호출한다.Business(Service): 목적에 맞게 데이터를 처리한다. 일반적으로 Repository의 다양한 데이터베이스 처리 메소드를 호출하여 저장,수정, 조회, 삭제 등을 수행한다.Data Access(Repository): 데이터베이스에 접근하여 작업을 요청한다. 다양한 데이터베이스 처리 방법을 제공하면 여러 서비스에서 공통적으로 사용할 수 있다.스프링 Bean과 의존성 주입(Dependency Injection)스프링에서 관리되는 객체, 즉 자바 클래스의 인스턴스를 의미제어의 역전(Inversion of Control), 줄여서 IoC이란?스프링 Bean으로 지정된 클래스는 개발자가 직접 인스턴스를 생성, 관리하지 않고 스프링 컨테이너가 주체가 됨Bean에서 다른 Bean을 사용하는 경우, 즉 의존하는 경우에 스프링 컨테이너에서 의존성을 주입해준다.의존성 주입이란?의존성 주입이란 한 객체가 사용하는 다른 객체를 객체 내부에서 직접 만들지 않고, 외부에서 주입받아 사용하는 방법스프링이 실행될때 컴포넌트 스캔이란 과정을 거쳐 Bean으로 지정된 클래스들의 객체를 모두 만들둔다.이 과정에서 한 Bean이 사용하는 다른 Bean이있다면 그 Bean을 먼저 생성⭐ IoC(역전의 제어) ⇒ 주로 주도권 스프링객체의 생성, 생명주기의 관리까지 모든 객체에 대한 제어권이 바뀌었다는 것을 의미컴포넌트 의존관계 설정(Component dependency resoulution), 설정(Configuration) 및 생명주기(LifeCycle)을 해결하기 위한 디자인 패턴(Design Pattern)이다.스프링이 직접 만든다. 스캔하여 heap 메모리에 올려주고 스프링이 직접 관리(++ 추가)⭐ IoC 컨테이너컨테이너?란 컨테이너는 보통 객체의 생명주기를 관리, 생성된 인스턴스들에게 추가적인 기능을 제공하도록 하는 것스프링 프레임워크도 객체를 생성하고 관리하고 책임지고 의존성을 관리해주는 컨테이너가 있는데,그것이 바로 IoC 컨테이너(=스프링 컨테이너)인스턴스 생성부터 소멸까지의 인스턴스 생명주기 관리를 개발자가 아닌 컨테이너가 대신 해주기 때문에 객체 관리 주체가 프레임워크(Container)가 되기 때문에 개발자는 로직에 집중할 수 있다는 장점이 있다.IoC 컨테이너는 객체의 생성을 책임지고, 의존성을 관리한다.POJO의 생성, 초기화, 서비스, 소멸에 대한 권한을 가진다.개발자들이 직접 POJO를 생성할 수 있지만 컨테이너에게 맡긴다.개발자는 비즈니스 로직에 집중할 수 있다.객체 생성 코드가 없으므로 TDD가 용이하다.POJO(Plain Old Java Object)란? 주로 특정 자바 모델이나 기능, 프레임워크를 따르지 않는 Java Object를 지칭한다. Java Bean 객체가 대표적이다. 간단하게 getter / setter를 생각하면 될 것 같다. 생성자 주입 권장런타임에 수정자를 호출해서 의존성이 바뀌는 것을 방지할 수 있다.순환참조 시 컴파일 오류가 발생해서 런타임 단계에서 메소드가 서로 호출하는 스택오버플로우 에러를 방지할 수 있다.의존하는 Bean이 누락되면 컴파일 오류가 발생하기 때문에 런타임에서 NullPointerException을 방지할 수 있다.수정자나 필드는 null이 넘어갈 수 있음[이론] 웹 서비스를 구성하는 요소클라이언트요청하는 주체브라우저서버응답하는 주체데이터베이스에서 데이터의 삽입, 수정, 조회, 삭제(CRUD)한 뒤 결과를 클라이언트에 반환하는 작업대부분의 규모 있는 서비스에서는 동일한 작업을 수행하는 여러 대의 컴퓨터, 서로 다른 작업을 수행하는 컴퓨터 등으로 서버 컴퓨터 집합(클러스터)을 구성3. 데이터베이스데이터의 집합을 저장하고 관리하는 역할을 하는 프로그램을 데이터베이스 관리 시스템브라우저를 주소창에 입력하면 어떤일이 일어날까?
백엔드
・
인프런강의
・
백엔드
・
코틀린
・
나만의포폴사이트