8일 차 TIL
오늘은 스프링 숙련 강의를 다 수강했다.
상품-폴더 간의 API들을 구현하고
페이징처리, 중복생성 체크 등을 배웠고
AWS를 통해 배포하는 방법을 배웠다.
이후엔 AWS의 원리에 대해서 좀 더 공부해 보고 배포도 몇 번 더 해봐야 할 것 같다.
내일은 스프링 심화 강의를 수강할 예정이다.
회원별 상품 API 구현
회원별로 관심 상품을 등록할 때, 로그인한 회원마다 본인의 관심 상품만 조회되어야 한다.
이때 관심 상품을 등록할 때, 회원 정보를 같이 Product에 저장하고
관심 상품을 조회할 때, 회원정보로 조회하면 된다.
관리자라면 모두 볼 수 있도록 특정 회원정보로 조회하는 것이 아닌 전체조회를 하면 된다.
페이징 처리
사용한 객체들 => Page, Pageable, Sort + Sort.Direction
사용자에게 받는 값(필터링) => sortBy, isAsc
Page 사용법 정리)
페이징 처리는
Page 객체를 사용하는데
Pageable로 만든다.
Pageable 은 PageRequest로 만들고
PageRequest의 of 메서드로 만든다.
of 메서드는 page, size, sort 정보가 필요한데
이는 현재 몇 페이지인지,
페이지당 몇 개를 보여줄지,
정렬은 오름차순인지 내림차순인지를 나타낸다.
이런 정렬 정보는 Sort 객체로 만든다.
Sort의 by 메서드를 사용하는데 파라미터로 정렬 방향과 정렬 기준을 넣어
정렬 방향은 Sort의 Direction 변수를 사용한다.
Direction은 ASC와 DESC가 있다.
그래서 사용자로부터 isAsc를 boolean 값으로 받고
isAsc 값에 따라 Direction 값을 ASC 나 DESC로 설정해서 넘겨주면 된다.
이렇게 만들어진 Pageable은 쿼리메서드에 반환값인 Page 객체를 만드는데 파라미터로 사용된다.
public Page<ProductResponseDto> getProducts(int page, int size, String sortBy, boolean isAsc){
Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
Sort sort = Sort.by(direction, sortBy);
Pageable pageable = PageRequest.of(page,size,sort);
Page<Product> productList;
productList = productRepository.findAll(pageable);
return productList.map(ProductResponseDto::new);
}
유저의 권한을 검사하고 싶다면?
회원가입할 때 같이 설정한
User 객체의 role 필드를 사용하면 된다. 이는 UserEnumRole 타입이다.
User 객체는 필터를 통해 UserDetailsImpl에 저장되고 인증 객체 생성에 사용되기도 한다.
- 인증 객체 생성 과정
인증 객체는 UsernamePasswordAuthenticationToken 객체인데,
Spring Security의 필터에 연결된 JwtAuthorization의 인증 객체 생성 메서드에서 생성된다.
이를 생성할 때 UserDetails 객체와 그 객체의 getAuthorities() 메서드를 같이 넘겨주게 된다.
UserDetail는 UserDetailsService를 구현한 UserDetailsServiceImpl에서 생성된다.
생성 시 회원 DB에서 User 객체를 가져와 파라미터로 전달한다.
이렇게 생성된 UserDetails 객체는 UserDetails를 구현한 UserDetailsImpl인데
이 객체의 getAuthorites() 메서드에는 직접 설정한 UserRoleEnum 클래스를 사용한다.
유저의 권한 검사가 적용된 페이징 처리 코드
public Page<ProductResponseDto> getProducts
(
User user,
int page,
int size,
String sortBy,
boolean isAsc
)
{
// 페이징 처리
Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
Sort sort = Sort.by(direction, sortBy);
Pageable pageable = PageRequest.of(page, size, sort);
// 사용자 권한 가져와서 ADMIN 이면 전체 조회, USER 면 본인이 추가한 부분 조회
UserRoleEnum userRoleEnum = user.getRole();
Page<Product> productList;
if (userRoleEnum == UserRoleEnum.USER) {
// 사용자 권한이 USER 일 경우
productList = productRepository.findAllByUser(user, pageable);
} else {
productList = productRepository.findAll(pageable);
}
return productList.map(ProductResponseDto::new);
}
User의 role 필드에 따라서, 특정 user의 관심상품리스트를 조회 or 전체유저의 관심상품리스트 조회로 나눌 수 있다.
ApplicationRunner
ApplicationRunner 인터페이스를 통해 서버가 실행될 때 실행하는 메서드 만들 수 있다.
인터페이스의 run 메서드를 재정의해서 사용한다.
run 메서드는 서버가 실행될 때 실행되므로 더미데이터를 추가하는 용도로 사용할 수도 있다.
@Component
public class TestDataRunner implements ApplicationRunner{
@Override
public void run(ApplicationArguments args){
// 서버 시작시 실행할 코드 ...
}
}
다대다 관계에서 중간 테이블 활용
관심 상품을 폴더에 저장하는 기능을 구현한다.
1. 폴더 생성 / 조회
2. 관심 상품에 폴더 추가 / 폴더 전체 조회
중간테이블을 직접 생성/관리해서 폴더 생성/조회/추가를 정리했다.
Product와 Folder는 N : M의 관계이므로 중간 테이블인 product_folder 테이블을 생성해서 관리한다.
이때, 상품 객체로 폴더를 조회하는 기능이 필요하다.
하지만 이 프로젝트에서는 폴더 객체에서 상품을 조회하진 않을 것이라
상품 -> 폴더의 단방향 관계이다.
중간 테이블인 Product_Folder에서는 상품과 폴더를 외래키로 관리한다.
- Product와 Folder객체를 @ManyToOne으로 매핑한다.
Product에서는 조회를 위해 @OneToMany로 매핑한다.
- 이때 List<ProductFolder> productFolderList = new ArrayList<>(); // 테이블에는 저장이 안 됨, 오직 조회용 필드임
그리고 Client에게 반환할 때 상품폴더 목록을 같이 전달한다.
1. 상품 컨트롤러에서 상품 목록 조회 메서드 실행
2. 상품 서비스에서 상품 목록을 먼저 조회함
3. 상품반환 Dto로 Client에게 반환
이때 상품반환 Dto에 폴더 목록을 저장함.
1. 상품 엔티티에서 상품폴더목록을 가져옴
2. 상품폴더 목록에서 폴더정보만 가져옴.
3. 다시 필드에 리스트로 폴더 정보들을 저장
이때 1대다 같은 경우엔 여러 개를 가져오기 때문에
만약 즉시 로딩으로 되어있다면?
그 안에 연관 매핑된 객체까지 받아오므로 너무 많은 데이터를 한 번에 가져오게 된다.
그래서 뒤에가 Many로 끝나는 것들은 기본 설정이 지연로딩이다.
그랬을 때, 1대다에서 해당 객체를 조회하려면 2가지 방법이 있다.
1. 지연로딩 조회 기능
2. fetch 옵션을 즉시로딩으로 바꾼다.
만약 상품과 폴더 간에서,
상품을 조회할 때마다 항상 폴더의 정보도 필요하다면? 즉시로딩
때에 따라서 필요할 수도 있고 아닐 수도 있다면? 지연로딩이 좋다.
만약 상품을 조회하는 메서드에서, 지연로딩을 통해 폴더정보를 가져오려면?
영속성 컨텍스트가 필요하기 때문에 @Transactional이 필요하다.
추가로 조회용이기 때문에 readOnly = true를 걸어서 성능을 높일 수도 있다.
관심 상품에 폴더 추가 기능
관심 상품에 폴더를 추가할 때,
즉 해당 상품을 폴더에 등록할 때
상품 정보 / 폴더 정보 / 유저 정보(로그인되어 있는 유저)가 필요하다.
이때, 폴더를 먼저 생성하고 그 폴더에 관심 상품을 추가하는 순서이므로
관심 상품에 폴더를 추가하기 위해서는 기본적으로
해당 상품 검증
해당 폴더 검증
해당 유저 검증이 필요하다.
이를 상품.유저.id == 유저.id || 폴더.유저.id == 유저.id 로 검증할 수 있다.
객체비교의 경우엔 equals를 사용한다.
보통 id는 Long이므로 객체이다. 따라서 equals를 사용한다.
그리고 관심 상품이 해당 폴더에 이미 등록되어 있는지에 대한 중복체크도 필요하다.
그렇게 검증이 됐다면?
관심 상품에 등록하는 것이 아닌 중간테이블인 Product_Folder 테이블에 저장하면 된다.
쿼리메서드
특정 user의 관심상품을 가져오는데,
product_folder 테이블에서 folderId 가 ? 인 상품 가져오는 법
Page<Product> products = findAllByUserAndProductFolderList_FolderId(user, folderId, pageable);
AWS로 배포하기
AWS로 배포하는 방법을 정리했다.
1. AWS 가입하기
https://portal.aws.amazon.com/billing/signup#/start
2. 로컬과 클라우드 서버 간 파일 관리를 위한 Filezilla 설치하기
https://filezilla-project.org/download.php
3. 클라우드 서버 원격 접속을 위한 Git bash 설치하기
4. AWS의 데이터베이스인 RDS 생성하기
https://ap-northeast-2.console.aws.amazon.com/console/home?region=ap-northeast-2#
- 표준 생성 + MySQL
- 프리티어
- 추가 연결 구성 > 퍼블릭 액세스 : 예 + 새로 생성
- 초기데이터베이스이름 설정
5. 생성된 RDS의 소스-Anywhere로 0.0.0.0/0으로 규칙 저장하기
6. 데이터베이스 엔드포인트로 IntelliJ에서 데이터소스 생성하기
Name : 상관없음
Host : 내 RDS의 엔드포인트
User : RDS 생성 시 입력한 마스터 사용자 이름
Password : RDS 생성 시 입력한 마스터 암호
database : RDS 생성 시 입력한 초기 데이터베이스 이름
7. 스프링부트 프로젝트 내의 application.properties 설정하기
spring.datasource.url=jdbc:mysql://나의엔드포인트:3306/초기데이터베이스이름
spring.datasource.username=나의User
spring.datasource.password=나의Password
spring.jpa.hibernate.ddl-auto=update
8. 클라우드 서버 사용을 위한 EC2 구매하기
https://ap-northeast-2.console.aws.amazon.com/ec2/v2/home?region=ap-northeast-2
- ubuntu 22.04 선택
- 키 생성하기 ( RSA, .pem )
9. Git bash 실행 후 클라우드 서버에 접속하기
ssh -i 키페어경로 ubuntu@AWS의publicIP
10. 클라우드 서버에 저장할 스프링 부트 파일 빌드하기
IntelliJ 우측 탭 Tasks > build > build 더블 클릭
build 폴더의 libs 안에 jar 파일 생성 확인
11. 파일 질라로 배포 파일 업로드하기
- 연결 후 로컬 파일을 드로그해서 클라우드 서버로 업로드
12. Git bash에서 스프링 부트 실행
- 클라우드 서버 접속 후, java -jar JAR파일명.jar
13. EC2 인스턴스에서 80, 8080 포트 열어주기
14. 내 컴퓨터에서 접속 테스트
- http://내EC2퍼블릭IP:8080
15. 80으로 오는 요청 8080으로 포트포워딩하기
iptables --version
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
15-1 포트포워딩 적용 안될 경우
which nft
sudo apt update
sudo apt install -y nftables
sudo systemctl enable nftables
sudo systemctl start nftables
sudo nft add table ip nat
sudo nft 'add chain ip nat prerouting { type nat hook prerouting priority 0; }'
sudo nft 'add chain ip nat postrouting { type nat hook postrouting priority 100; }'
sudo nft add rule ip nat prerouting tcp dport 80 redirect to 8080
15-2 적용 확인하기
sudo nft list ruleset
- 실행결과에서 tcp dport 80 redirect to :8080 추가된 거 확인하기
table inet filter{
chain input {
type filter hook input priority filter; policy accept;
}
chain forward {
type filter hook forward priority filter; policy accept;
} chain output {
type filter hook output priority filter; policy accept;
}
}
table ip nat {
chain prerouting {
type nat hook prerouting priority filter; policy accept;
tcp dport 80 redirect to :8080
} chain output {
type nat hook output priority filter; policy accept;
}
}
16. 서비스 재 시작 후, 포트 빼고 접속해 보기
17. 클라우드 서버에서 계속 돌아가게 하기
nohup java -jar JAR파일명.jar &
18. 서비스 종료하기
# 아래 명령어로 미리 pid 값(프로세스 번호)을 본다
ps -ef | grep java
# 아래 명령어로 특정 프로세스를 죽인다
kill -9 [pid값]
19. 다시 켜기
nohup java -jar JAR파일명.jar &
'내배캠' 카테고리의 다른 글
| [내일배움캠프 본캠프] 10일 차 TIL Spring AI (0) | 2026.02.19 |
|---|---|
| [내일배움캠프 본캠프] 9일 차 TIL 소셜 로그인(OAuth), 단위 테스트, AOP, 예외 처리 (0) | 2026.02.18 |
| [내일배움캠프 본캠프] 7일 차 TIL 지연로딩, cascade, scheduler, 헤더 JWT (0) | 2026.02.16 |
| [내일배움캠프 본캠프]6일 차 TIL RestTemplate, Entity 연관 관계 (0) | 2026.02.16 |
| [내일배움캠프 본캠프] 5일 차 TIL Spring Security + JWT (0) | 2026.02.14 |
