컨트롤러


    @PostMapping("/signup")
    public ApiResult signup(@RequestBody SignupRequestDto requestDto) {
        userService.signup(requestDto);
        return new ApiResult("회원가입 성공", HttpStatus.OK.value());
    }

    @PostMapping("/login")
    public ApiResult login(@RequestBody LoginRequestDto requestDto, HttpServletResponse response) {
        userService.login(requestDto, response);
        return new ApiResult("로그인 성공", HttpStatus.OK.value());
    }
}

 

서비스

    public void signup(SignupRequestDto requestDto) {
        String username = requestDto.getUsername();
        String password = requestDto.getPassword();
        String role = requestDto.getRole();

        // 회원 중복 확인
        Optional<User> found = userRepository.findByUsername(username);
        if (found.isPresent()) {
            throw new HanghaeBlogException(HanghaeBlogErrorCode.IN_USED_USERNAME, null);
        }

        User userEntity = new User(username, password, role);
        userRepository.save(userEntity);
    }​
    public void login(LoginRequestDto requestDto, HttpServletResponse response) {
        String username = requestDto.getUsername();
        String password = requestDto.getPassword();

        User user = userRepository.findByUsername(username).orElseThrow(
                () -> new HanghaeBlogException(HanghaeBlogErrorCode.NOT_FOUND_USER, null)
        );

        if (!user.getPassword().equals(password)) {
            throw new HanghaeBlogException(HanghaeBlogErrorCode.WRONG_PASSWORD, null);
        }

        response.addHeader(JwtUtil.AUTHORIZATION_HEADER, jwtUtil.createToken(user.getUsername()));
    }

 

 

JWT에서 유저 정보 조회

    public User checkToken(HttpServletRequest request) {

        String token = this.resolveToken(request);
        Claims claims;

        User userEntity = null;

        if (token != null) {
            if (this.validateToken(token)) {
                // 토큰에서 사용자 정보 가져오기
                claims = this.getUserInfoFromToken(token);

            } else {
                throw new HanghaeBlogException(HanghaeBlogErrorCode.INVALID_TOKEN, null);
            }

            // 토큰에서 가져온 사용자 정보를 사용하여 DB 조회
            userEntity = userRepository.findByUsername(claims.getSubject()).orElseThrow(
                    () -> new HanghaeBlogException(HanghaeBlogErrorCode.NOT_FOUND_USER, null)
            );
        }
        return userEntity;
    }

'TIL > WEEK7' 카테고리의 다른 글

영속성 전이 Cascade  (0) 2023.06.29
개인과제 글 수정 기능 구현  (0) 2023.06.27
쿠키, 세션, 토큰, JWT  (0) 2023.06.26
package com.sparta.blog.entity;

import com.sparta.blog.dto.PostRequestDto;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;


@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "posts")
public class Post extends Timestamped {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String content;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;


    public Post(PostRequestDto postRequestDto, User user) {
        this.title = postRequestDto.getTitle();
        this.content = postRequestDto.getContent();
        this.user = user;
    }
}
package com.sparta.blog.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name="users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private String username;
    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    @Enumerated(value = EnumType.STRING)
    private UserRoleEnum role;

    @OneToMany(mappedBy = "user")
    private List<Post> postList = new ArrayList<>();

    public User(String username, String password, UserRoleEnum role) {
        this.username = username;
        this.password = password;
        this.role = role;
    }
}
   @PutMapping("/post/{id}")
    public PostResponseDto updatePost(@PathVariable Long id, @RequestBody PostRequestDto requestDto, @AuthenticationPrincipal UserDetailsImpl userDetails) {
        return postService.updatePost(id, requestDto, userDetails.getUser());
    }
    @Transactional
    public PostResponseDto updatePost(Long id, PostRequestDto requestDto, User user) {
        Post post = findPost(id);
        if (!post.getUser().getId().equals(user.getId())) {
            throw new IllegalArgumentException("다른 회원의 글은 수정할 수 없습니다.");
        } else {
            post.setTitle(requestDto.getTitle());
            post.setContent(requestDto.getContent());
            postRepository.save(post);
        }
        return new PostResponseDto(post);
    }

user entity의 postList도 업데이트 되도록 수정할 예정

'TIL > WEEK7' 카테고리의 다른 글

영속성 전이 Cascade  (0) 2023.06.29
개인과제 회원 가입, 로그인 구현  (0) 2023.06.28
쿠키, 세션, 토큰, JWT  (0) 2023.06.26

쿠키

그냥 옮기는 매개체, 사용자가 조작해도 크게 문제되지 않을 정보를 브라우저에 저장

 

 

세션

인증에 대한 정보를 서버가 저장

 

 

토큰

서버가 기억하는 이상하게 생긴 스트링, ID카드처럼 서버에게 보여줘야 하는 것, 인증에 대한 정보를 사용자가 저장

 

 

 

JWT

정보를 가지고 있는 토큰. DB 없이 검증할 수 있음

 

'TIL > WEEK7' 카테고리의 다른 글

영속성 전이 Cascade  (0) 2023.06.29
개인과제 회원 가입, 로그인 구현  (0) 2023.06.28
개인과제 글 수정 기능 구현  (0) 2023.06.27

1. 인증설정 (WebSecurityConfig.java)

WebSecurityConfig > http.formLogin() > UsernamePasswordAuthenticationFilter > SecurityFilterChain > 요청별 인증수행
 


2. 인증정보 받아오기 & 인증객체에 넣기 (UserDetailsServiceImpl.java)

UsernamePasswordAuthenticationFilter > UserDetailsService 구현 > loadUserByUsername() > User > UserDetails > Authentication (createSuccessAuthentication()에서 만들어짐)

 

3. 인증정보 구현체 (UserDetailsImpl.java)

UserDetails 구현체 > UserDetailsImpl
 

 

4. 인증객체(Authentication)에서 UserDetails 값 받아오기 (@AuthenticationPrincipal)

Authentication > getPrincipal() > UserDetails > user

'TIL > WEEK6' 카테고리의 다른 글

양방향 매핑 규칙  (0) 2023.06.22

 

 

어느 쪽을 연관 관계 주인으로 정해야 할까?

외래키를 가지고 있는 쪽을 주인으로 정한다!

일반적으로 N:1일 때 N인 쪽에 외래키를 준다.

 

 

DB처럼 양쪽에서 모두 조회가 되도록 하기 위해서 양방향 매핑을 한다.

가급적 단방향으로 매핑하는 게 좋지만 양방향으로 매핑해야 한다면 일단 단방향으로 매핑해 놓고 다음에 양방향 매핑을 적용한다.

'TIL > WEEK6' 카테고리의 다른 글

Spring Security  (0) 2023.06.23

build.gradle의 dependecies에 아래 코드 추가

    // JPA 설정
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    // MySQL
    implementation 'mysql:mysql-connector-java:8.0.28'

 

application.properties에 DB 정보 추가

spring.datasource.url=jdbc:mysql://localhost:3306/DB명
spring.datasource.username=root
spring.datasource.password=비밀번호
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

 

Entity 클래스 작성

package com.sparta.memo.entity;

import com.sparta.memo.dto.MemoRequestDto;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity // JPA가 관리할 수 있는 Entity 클래스 지정
@Getter
@Setter
@Table(name = "memo") // 매핑할 테이블의 이름을 지정
@NoArgsConstructor
public class Memo {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "username", nullable = false)
    private String username;
    @Column(name = "contents", nullable = false, length = 500)
    private String contents;

    public Memo(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }

    public void update(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }
}

 

레포지토리를 JpaRepository를 상속받는 인터페이스로 수정

package com.sparta.blog.repository;

import com.sparta.blog.entity.Post;
import org.springframework.data.jpa.repository.JpaRepository;


public interface PostRepository extends JpaRepository<Post, Long> {

}

 

 

컨트롤러 클래스 수정

package com.sparta.memo.controller;

import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.service.MemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api")
public class MemoController {

    private final MemoService memoService;

    @Autowired
    public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }

    @PostMapping("/memos")
    public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
        return memoService.createMemo(requestDto);
    }

    @GetMapping("/memos")
    public List<MemoResponseDto> getMemos() {
        return memoService.getMemos();
    }

    @PutMapping("/memos/{id}")
    public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
        return memoService.updateMemo(id, requestDto);
    }

    @DeleteMapping("/memos/{id}")
    public Long deleteMemo(@PathVariable Long id) {
        return memoService.deleteMemo(id);
    }

}

 

서비스 클래스 수정

package com.sparta.memo.service;

import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.entity.Memo;
import com.sparta.memo.repository.MemoRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class MemoService {

    private final MemoRepository memoRepository;

    public MemoService(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }

    public MemoResponseDto createMemo(MemoRequestDto requestDto) {
        // RequestDto -> Entity
        Memo memo = new Memo(requestDto);

        // DB 저장
        Memo saveMemo = memoRepository.save(memo);

        // Entity -> ResponseDto
        MemoResponseDto memoResponseDto = new MemoResponseDto(saveMemo);

        return memoResponseDto;
    }

    public List<MemoResponseDto> getMemos() {
        // DB 조회
        return memoRepository.findAllByOrderByModifiedAtDesc().stream().map(MemoResponseDto::new).toList();
    }

    @Transactional
    public Long updateMemo(Long id, MemoRequestDto requestDto) {
        // 해당 메모가 DB에 존재하는지 확인
        Memo memo = findMemo(id);

        // memo 내용 수정
        memo.update(requestDto);

        return id;
    }

    public Long deleteMemo(Long id) {
        // 해당 메모가 DB에 존재하는지 확인
        Memo memo = findMemo(id);

        // memo 삭제
        memoRepository.delete(memo);

        return id;
    }

    private Memo findMemo(Long id) {
        return memoRepository.findById(id).orElseThrow(() ->
                new IllegalArgumentException("선택한 메모는 존재하지 않습니다.")
        );
    }
}

'TIL > WEEK5' 카테고리의 다른 글

IoC와 DI  (1) 2023.06.14
1주차 숙제  (0) 2023.06.13
Spring 입문 1주차  (0) 2023.06.12

IoC(Inversion of Control) 제어의 역전

개발자가 직접 객체의 생성과 소멸 객체간 관계같은 객체의 제어를 수행하는 것이 아니라, 여러 프레임워크, 컨테이너에서 제어를 수행하는 것

 

 

DI(Dependency Injection) 의존성 주입

객체를 직접 생성하지 않고 외부에서 미리 만든 객체를 주입하는 것 → 클래스 간 결합을 느슨하게 해서 어떤 클래스의 변경이 그 클래스를 이용하는 다른 클래스에 미치는 영향을 줄인다.

 

  • ⭐️⭐️⭐️생성자를 통한 주입(거의 이거 씀)

Consumer consumer = new Consumer(new Chicken()); 와 같이 생성자를 통해 주입

public class Consumer {

    Food food;

    public Consumer(Food food) {
        this.food = food;
    }

    void eat() {
        this.food.eat();
    }

    public static void main(String[] args) {
        Consumer consumer = new Consumer(new Chicken());
        consumer.eat();

        consumer = new Consumer(new Pizza());
        consumer.eat();
    }
}

interface Food {
    void eat();
}

class Chicken implements Food{
    @Override
    public void eat() {
        System.out.println("치킨을 먹는다.");
    }
}

class Pizza implements Food{
    @Override
    public void eat() {
        System.out.println("피자를 먹는다.");
    }
}

 

 

 

  • 메서드를 통한 주입

consumer.setFood(new Chicken()); 와 같이 setFood 메서드로 주입

public class Consumer {

    Food food;

    void eat() {
        this.food.eat();
    }

    public void setFood(Food food) {
        this.food = food;
    }

    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        consumer.setFood(new Chicken());
        consumer.eat();

        consumer.setFood(new Pizza());
        consumer.eat();
    }
}

interface Food {
    void eat();
}

class Chicken implements Food{
    @Override
    public void eat() {
        System.out.println("치킨을 먹는다.");
    }
}

class Pizza implements Food{
    @Override
    public void eat() {
        System.out.println("피자를 먹는다.");
    }
}

 

 

  • 필드에 직접 주입(잘 안 씀)

consumer.food = new Chicken();

public class Consumer {

    Food food;

    void eat() {
        this.food.eat();
    }

    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        consumer.food = new Chicken();
        consumer.eat();

        consumer.food = new Pizza();
        consumer.eat();
    }
}

interface Food {
    void eat();
}

class Chicken implements Food{
    @Override
    public void eat() {
        System.out.println("치킨을 먹는다.");
    }
}

class Pizza implements Food{
    @Override
    public void eat() {
        System.out.println("피자를 먹는다.");
    }
}

 

 

  • +) Lombok 사용

클래스 위에 @RequiredArgsConstructor를 달고 멤버 변수에 final을 달아준다.

@Component
@RequiredArgsConstructor // final로 선언된 멤버 변수를 파라미터로 사용하여 생성자를 자동으로 생성합니다.
public class MemoService {
    private final MemoRepository memoRepository;
}

 

 

 

 

IoC Container 

bean들을 모아둔 하나의 컨테이너

 

 

Bean

스프링이 관리하고 있는 객체, 이름은 클래스 명에서 첫 글자를 소문자로 바꾼 것으로 결정된다.

  • 클래스 위에 @Component를 달면 bean으로 등록된다.

3 Layer Annotation

Controller, Service, Repository의 역할로 구분된 클래스들은 @Component 대신 각각

  1. @Controller, @RestController
  2. @Service
  3. @Repository

를 달아주면 된다.

 

  • 객체의 의존성을 가지는 부분(객체를 주입 받는 부분)에 @Autowired를 달아준다. 생성자가 1개일 땐 생략 가능하다.
@RestController
@RequestMapping("/api")
public class MemoController {

    private final MemoService memoService;

    @Autowired
    public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }
    
    ....
}

위 코드에서 생성자는 MemoController(MemoService memoService) 하나이므로 @Autowired를 생략해도 된다.

 

 

 

 

 

'TIL > WEEK5' 카테고리의 다른 글

SpringBoot의 JPA  (0) 2023.06.15
1주차 숙제  (0) 2023.06.13
Spring 입문 1주차  (0) 2023.06.12
cd /usr/local/mysql/bin
./mysql -u root -p

MySQL 설치 시 입력한 root 사용자의 비밀번호를 입력

USE 데이터베이스 이름;

 

'SQL' 카테고리의 다른 글

실전에서 유용한 SQL 문법 (문자열, Case)  (0) 2023.05.04
SQL 4주차 - Subquery  (0) 2023.05.04
SQL 2주차 - GROUP BY, ORDER BY  (0) 2023.05.04
SQL 3주차 - JOIN, UNION  (0) 2023.05.02
SQL 1주차 - BETWEEN, IN, LIKE, LIMIT  (0) 2023.04.27

+ Recent posts