본문 바로가기

과제를하면서 트러블슈팅

일정 관리 앱 만들기 devlop

반응형
SMALL

네안녕하세요 이번에는 저번에 만든 일정관리앱에 이어서 일정 관리 앱 만들기 devlo과제를

만들었습니다..

이번과제를하면서 난이도와 배우는양이 너무많아 시간들이 촉박해서 TIL을 작성하지 못했는데 이제 좀 시간이 생길거같아서 아마 다시 작성하지 않을까 싶습니다

 우선 이번일정관리앱이 저번과 다른점은 JPA를쓴다는건데요

JPA를사용하면 우리가저번일정관리앱에서 쓴 CRUD를 쉽게 자동으로 구현해준다는 점입니다

package org.example.scheduledevelop.repository;

import org.example.scheduledevelop.entity.Schedule;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ScheduleRepository extends JpaRepository<Schedule, Long> {

}

 

package org.example.scheduledevelop.repository;

import org.example.scheduledevelop.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);

    Optional<User> findByEmail(String email); // Optional<User>로 변경
}

이부분을 보시면 JPA를 사용해 정말 간단해진걸 보실수있습니다.

그리고 달라진점 인증 인가가 생겻다는점

그로인해 로그인 로그아웃 회원가입이 생겻다는점입니다

우선 필수기능가이드입니다.

여기까지가 제가만든 필수과제 입니다

package org.example.scheduledevelop.config;

import org.example.scheduledevelop.filter.AuthenticationFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<AuthenticationFilter> authenticationFilter() {
        FilterRegistrationBean<AuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new AuthenticationFilter());
        registrationBean.addUrlPatterns("/schedules/*");  // 필터를 적용할 URL 패턴
        return registrationBean;
    }
}
package org.example.scheduledevelop.controller;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.example.scheduledevelop.dto.LoginRequestDto;
import org.example.scheduledevelop.service.LoginService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {

    private final LoginService loginService;

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequestDto loginRequest, HttpServletRequest request) {
        // 로그인 서비스에 HttpServletRequest를 전달
        return loginService.login(loginRequest, request);  // HttpServletRequest를 전달하여 로그인 처리
    }

    @PostMapping("/logout")
    public ResponseEntity<String> logout(HttpServletRequest request) {
        // 세션 삭제 처리
        return loginService.logout(request);
    }
}
package org.example.scheduledevelop.controller;

import org.example.scheduledevelop.dto.ScheduleRequestDto;
import org.example.scheduledevelop.dto.ScheduleResponseDto;
import org.example.scheduledevelop.service.ScheduleService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/schedules")  // 일정 관련 API 경로
public class ScheduleController {

    private final ScheduleService scheduleService;

    // 생성자 주입을 통해 ScheduleService를 주입받음
    public ScheduleController(ScheduleService scheduleService){
        this.scheduleService = scheduleService;
    }

    // 일정 생성 (POST)
    @PostMapping
    public ResponseEntity<ScheduleResponseDto> createSchedule(@RequestBody ScheduleRequestDto dto) {
        // 서비스 메서드 호출하여 일정 생성
        ScheduleResponseDto response = scheduleService.createSchedule(dto);
        // 생성된 일정 정보와 상태 코드(201 Created) 반환
        return new ResponseEntity<>(response, HttpStatus.CREATED);
    }

    // 일정 조회 (단건, GET)
    @GetMapping("/{id}")
    public ResponseEntity<ScheduleResponseDto> findScheduleById(@PathVariable Long id) {
        // 서비스 메서드로 일정을 찾고 응답
        ScheduleResponseDto response = scheduleService.getScheduleById(id);
        // 상태 코드(200 OK)로 응답 반환
        return new ResponseEntity<>(response, HttpStatus.OK);
    }

    // 모든 일정 조회 (GET)
    @GetMapping
    public ResponseEntity<List<ScheduleResponseDto>> getAllSchedules() {
        // 모든 일정 조회
        List<ScheduleResponseDto> schedules = scheduleService.getAllSchedules();
        // 상태 코드(200 OK)로 응답 반환
        return new ResponseEntity<>(schedules, HttpStatus.OK);
    }

    // 일정 수정 (PUT)
    @PutMapping("/{id}")
    public ResponseEntity<?> updateSchedule(@PathVariable Long id, @RequestBody ScheduleRequestDto scheduleRequestDto) {
        if (id == null) {
            return ResponseEntity.badRequest().body("ID cannot be null"); // String을 응답 본문으로 처리
        }
        // 서비스 메서드로 일정 수정
        ScheduleResponseDto updatedSchedule = scheduleService.updateSchedule(id, scheduleRequestDto);
        return updatedSchedule != null ? ResponseEntity.ok(updatedSchedule) : ResponseEntity.notFound().build();
    }




    // 일정 삭제 (DELETE)
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteSchedule(@PathVariable Long id) {
        // 서비스 메서드로 일정 삭제
        scheduleService.deleteSchedule(id);
        // 삭제 후 상태 코드(204 No Content) 반환
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
}
package org.example.scheduledevelop.controller;

import org.example.scheduledevelop.dto.UserDto;
import org.example.scheduledevelop.dto.UserRequestDto;
import org.example.scheduledevelop.dto.UserResponseDto;
import org.example.scheduledevelop.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")  // 사용자 관련 API 경로
public class UserController {

    @Autowired
    private UserService userService;  // UserService를 자동 주입

    // 사용자 생성 (POST)
    @PostMapping
    public ResponseEntity<UserDto> createUser(@RequestBody UserRequestDto userRequestDto) {
        // 서비스 메서드 호출하여 사용자 생성
        UserDto createdUser = userService.createUser(userRequestDto);
        // 생성된 사용자 정보와 상태 코드(201 Created) 반환
        return ResponseEntity.status(201).body(createdUser);
    }

    // 전체 유저 조회
    @GetMapping
    public ResponseEntity<List<UserResponseDto>> getAllUsers() {
        List<UserResponseDto> users = userService.getAllUsers();
        return new ResponseEntity<>(users, HttpStatus.OK);
    }

    // 사용자 조회 (단건, GET)
    @GetMapping("/{id}")
    public ResponseEntity<UserDto> getUserById(@PathVariable Long id) {
        // 서비스 메서드로 사용자 조회
        UserDto userDto = userService.getUserById(id);
        // 사용자 존재하면 200 OK 반환, 없으면 404 Not Found 반환
        return userDto != null ? ResponseEntity.ok(userDto) : ResponseEntity.notFound().build();
    }

    // 사용자 수정 (PUT)
    @PutMapping("/{id}")
    public ResponseEntity<UserDto> updateUser(@PathVariable Long id, @RequestBody UserRequestDto userRequestDto) {
        // 서비스 메서드로 사용자 수정
        UserDto updatedUser = userService.updateUser(id, userRequestDto);
        // 수정된 사용자 정보 반환
        return updatedUser != null ? ResponseEntity.ok(updatedUser) : ResponseEntity.notFound().build();
    }

    // 사용자 삭제 (DELETE)
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        // 서비스 메서드로 사용자 삭제
        userService.deleteUser(id);
        // 삭제 후 상태 코드(204 No Content) 반환
        return ResponseEntity.noContent().build();
    }
}
package org.example.scheduledevelop.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class LoginRequestDto {
    private String email;
    private String password;
}

package org.example.scheduledevelop.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class ScheduleRequestDto {

    private Long id;  // 일정 ID 추가
    private Long userId;  // 유저 ID 추가
    private String task;
    private String description;

}
package org.example.scheduledevelop.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.example.scheduledevelop.entity.Schedule;

import java.time.LocalDateTime;

@Getter
@AllArgsConstructor
public class ScheduleResponseDto {

    private Long id;
    private String task;
    private String description;
    private String username;  // User의 이름 추가

    public ScheduleResponseDto(Schedule schedule) {
        this.id = schedule.getId();
        this.task = schedule.getTask();
        this.description = schedule.getDescription();
        this.username = schedule.getUser().getUsername();  // User의 이름 포함
    }

}
package org.example.scheduledevelop.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class UserDto {
    private Long id;               // 사용자 ID
    private String username;       // 사용자 이름
    private String email;          // 사용자 이메일
    private String role;           // 사용자 역할 (USER, ADMIN 등)
    private LocalDateTime createdAt; // 생성일
    private LocalDateTime updatedAt; // 수정일

    // 사용자를 생성하는 DTO 생성자
    public UserDto(String username, String email, String role) {
        this.username = username;
        this.email = email;
        this.role = role;
    }
}
package org.example.scheduledevelop.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class UserRequestDto {
    private String username;  // 사용자 이름
    private String email;     // 사용자 이메일
    private String password;  // 사용자 비밀번호
    private String role;      // 사용자 역할 (USER, ADMIN 등)
}
package org.example.scheduledevelop.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class UserResponseDto {

    private Long id;
    private String username;
    private String role;

}
package org.example.scheduledevelop.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.example.scheduledevelop.entity.User;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Entity
@Getter
@NoArgsConstructor
public class Schedule {

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

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")  // User와의 관계 설정
    private User user;

    private String task;
    private String description;

    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime modifiedDate;

    public Schedule(User user, String task, String description) {
        this.user = user;
        this.task = task;
        this.description = description;
    }

    public void updateSchedule(User user, String task, String description) {
        this.user = user;
        this.task = task;
        this.description = description;
    }
}
package org.example.scheduledevelop.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Entity
@Getter
@Setter
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class) // JPA Auditing 활성화
public class User {

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

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

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

    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String role;

    @CreatedDate
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column(nullable = false)
    private LocalDateTime updatedAt;

    // 파라미터화된 생성자 추가
    public User(String username, String email, String password, String role) {
        this.username = username;
        this.email = email;
        this.password = password;
        this.role = role;
    }

    // 비밀번호를 반환하는 메서드
    public String getPassword() {
        return this.password;
    }
}
package org.example.scheduledevelop.filter;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;

@WebFilter(filterName = "authenticationFilter", urlPatterns = "/schedules/*")  // /schedules/* 경로만 필터링
public class AuthenticationFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;

        // 로그인 및 회원가입 요청은 인증 처리에서 제외
        String requestURI = httpRequest.getRequestURI();
        if (requestURI.startsWith("/auth/login") || requestURI.startsWith("/auth/signup")) {
            chain.doFilter(servletRequest, servletResponse);
            return;
        }

        // 인증 여부 체크: 세션에서 "userEmail"을 확인
        HttpSession session = httpRequest.getSession(false);
        if (session == null || session.getAttribute("userEmail") == null) {
            ((HttpServletResponse) servletResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
            return;
        }

        chain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 필터 초기화 (현재는 비어 있음)
    }

    @Override
    public void destroy() {
        // 필터 종료 (현재는 비어 있음)
    }
}
package org.example.scheduledevelop.repository;

import org.example.scheduledevelop.entity.Schedule;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ScheduleRepository extends JpaRepository<Schedule, Long> {

}
package org.example.scheduledevelop.repository;

import org.example.scheduledevelop.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);

    Optional<User> findByEmail(String email); // Optional<User>로 변경
}
package org.example.scheduledevelop.service;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.example.scheduledevelop.dto.LoginRequestDto;
import org.example.scheduledevelop.entity.User;
import org.example.scheduledevelop.repository.UserRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
@RequiredArgsConstructor
public class LoginService {

    private final UserRepository userRepository;

    public ResponseEntity<String> login(LoginRequestDto loginRequestDto, HttpServletRequest request) {
        // 이메일로 사용자 검색
        Optional<User> optionalUser = userRepository.findByEmail(loginRequestDto.getEmail());

        if (optionalUser.isEmpty()) {
            // 이메일이 일치하지 않는 경우
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid email or password");
        }

        User user = optionalUser.get();

        // 비밀번호가 일치하지 않는 경우
        if (!user.getPassword().equals(loginRequestDto.getPassword())) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid email or password");
        }

        // 로그인 성공 시 세션에 사용자 이메일 저장
        HttpSession session = request.getSession(true);  // 세션 없으면 새로 생성
        session.setAttribute("userEmail", user.getEmail());

        return ResponseEntity.ok("Login successful");
    }

    // 로그아웃 기능 (세션 삭제)
    public ResponseEntity<String> logout(HttpServletRequest request) {
        HttpSession session = request.getSession(false); // false로 하면 세션이 없으면 새로 생성되지 않음
        if (session != null) {
            session.invalidate();  // 세션 삭제
        }
        return ResponseEntity.ok("Logout successful");
    }
}

package org.example.scheduledevelop.service;

import lombok.RequiredArgsConstructor;
import org.example.scheduledevelop.dto.ScheduleRequestDto;
import org.example.scheduledevelop.dto.ScheduleResponseDto;
import org.example.scheduledevelop.dto.UserResponseDto;
import org.example.scheduledevelop.entity.Schedule;
import org.example.scheduledevelop.entity.User;
import org.example.scheduledevelop.repository.ScheduleRepository;
import org.example.scheduledevelop.repository.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ScheduleService {

    private final ScheduleRepository scheduleRepository;
    private final UserRepository userRepository;


    @Transactional
    public ScheduleResponseDto createSchedule(ScheduleRequestDto requestDto) {
        // 1. User 정보 가져오기 (userId로 조회)
        User user = userRepository.findById(requestDto.getUserId())
                .orElseThrow(() -> new IllegalArgumentException("User not found with id: " + requestDto.getUserId()));

        // 2. Schedule 객체 생성
        Schedule schedule = new Schedule(user, requestDto.getTask(), requestDto.getDescription());

        // 3. 일정 저장
        Schedule savedSchedule = scheduleRepository.save(schedule);

        // 4. 저장된 일정 정보 반환
        return new ScheduleResponseDto(savedSchedule);
    }

    @Transactional(readOnly = true)
    public List<ScheduleResponseDto> getAllSchedules() {
        List<Schedule> schedules = scheduleRepository.findAll();
        return schedules.stream()
                .map(ScheduleResponseDto::new)
                .collect(Collectors.toList());
    }
    public List<UserResponseDto> getAllUsers() {
        List<User> users = userRepository.findAll();
        return users.stream()
                .map(user -> new UserResponseDto(user.getId(), user.getUsername(), user.getRole()))
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public ScheduleResponseDto getScheduleById(Long id) {
        Schedule schedule = scheduleRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Schedule not found with id: " + id));
        return new ScheduleResponseDto(schedule);
    }

    @Transactional
    public ScheduleResponseDto updateSchedule(Long id, ScheduleRequestDto requestDto) {
        Schedule schedule = scheduleRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Schedule not found with id: " + id));

        // 1. User 정보 가져오기 (userId로 조회)
        User user = userRepository.findById(requestDto.getUserId())
                .orElseThrow(() -> new IllegalArgumentException("User not found with id: " + requestDto.getUserId()));

        // 2. Schedule 정보 수정
        schedule.updateSchedule(user, requestDto.getTask(), requestDto.getDescription());

        // 3. 수정된 일정 정보 반환
        return new ScheduleResponseDto(schedule);
    }

    @Transactional
    public void deleteSchedule(Long id) {
        Schedule schedule = scheduleRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Schedule not found with id: " + id));

        scheduleRepository.delete(schedule);
    }
}
package org.example.scheduledevelop.service;

import org.example.scheduledevelop.dto.UserDto;
import org.example.scheduledevelop.dto.UserRequestDto;
import org.example.scheduledevelop.dto.UserResponseDto;
import org.example.scheduledevelop.entity.User;
import org.example.scheduledevelop.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    // 사용자 생성
    public UserDto createUser(UserRequestDto userRequestDto) {
        // DTO -> 엔티티 변환 후 저장
        User user = new User(userRequestDto.getUsername(), userRequestDto.getEmail(), userRequestDto.getPassword(), userRequestDto.getRole());
        userRepository.save(user);

        // 엔티티에서 DTO로 변환하여 반환
        return new UserDto(user.getId(), user.getUsername(), user.getEmail(), user.getRole(), user.getCreatedAt(), user.getUpdatedAt());
    }

    // 사용자 조회
    public UserDto getUserById(Long id) {
        User user = userRepository.findById(id).orElse(null);
        if (user == null) {
            return null;
        }
        // 비밀번호 제외한 DTO 반환
        return new UserDto(user.getId(), user.getUsername(), user.getEmail(), user.getRole(), user.getCreatedAt(), user.getUpdatedAt());
    }

    // 전체 사용자 조회
    public List<UserResponseDto> getAllUsers() {
        List<User> users = userRepository.findAll();
        return users.stream()
                .map(user -> new UserResponseDto(user.getId(), user.getUsername(), user.getRole()))
                .collect(Collectors.toList());
    }

    // 사용자 수정
    public UserDto updateUser(Long id, UserRequestDto userRequestDto) {
        User existingUser = userRepository.findById(id).orElse(null);
        if (existingUser == null) {
            return null;
        }

        // 수정할 부분만 업데이트
        existingUser.setUsername(userRequestDto.getUsername());
        existingUser.setEmail(userRequestDto.getEmail());
        existingUser.setRole(userRequestDto.getRole());

        // 비밀번호가 비어있지 않으면 비밀번호도 수정
        if (userRequestDto.getPassword() != null && !userRequestDto.getPassword().isEmpty()) {
            existingUser.setPassword(userRequestDto.getPassword());
        }

        // 수정된 사용자 저장
        userRepository.save(existingUser);

        // 업데이트된 사용자 정보 반환
        return new UserDto(existingUser.getId(), existingUser.getUsername(), existingUser.getEmail(), existingUser.getRole(), existingUser.getCreatedAt(), existingUser.getUpdatedAt());
    }

    // 사용자 삭제
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

이렇게 위와같이 코드를 짜주었고

이게 기한에 맞춰 하기에 매번 새벽까지 코드를 짜다보니...

만든날마다 트러블 슈팅을 해줘야하는데

시간이 너무 촉박하고 다음날 오전9시부터 다시 수업시작이다보니 시간이모자라서 

과제가 다끝난뒤에 쓰니까 문제는 많았는데 기억이 많이안나서 문제 였습니다.

이번트러블슈팅은 아쉽지만 그냥 과제결과물만 보여드려야될거같고

다음 과제때 하나하나 풀어가는 모습을 보여드려야할거같습니다.

감사합니다

 

반응형
LIST

'과제를하면서 트러블슈팅' 카테고리의 다른 글

일정 관리 앱 만들기  (0) 2025.02.03
키오스크 만들기 Lv5  (2) 2025.01.20
키오스크 만들기 Lv4  (0) 2025.01.17
키오스크 만들기 Lv3  (0) 2025.01.16
키오스크 만들기  (3) 2025.01.15