일정관리 앱-과제3

ChoRong0824·2025년 2월 12일
0

Web

목록 보기
30/51


회원가입 및 로그인 API는 인증 없이 접근할 수 있도록 허용해야 접근 가능하다.
SEcurityConfig에 추가해줬다.

 @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())  // CSRF 비활성화
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))  // 세션 기반 인증 사용
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/users/signup", "/api/users/login").permitAll()  // 회원가입, 로그인은 인증 없이 허용
                .anyRequest().authenticated())  // 나머지 요청은 인증 필요
            .formLogin(login -> login.disable())  // 기본 로그인 폼 비활성화
            .httpBasic(httpBasic -> httpBasic.disable());  // HTTP Basic 인증 비활성화

        return http.build();
    }

이제 정상 동작하는 것을 확인할 수 있다.

로그인 API(/api/users/login)가 인증 없이 접근할 수 있도록 Security 설정을 수정해줬기 때문에, 무엇이 문제인지 엔드포인트를 먼저 확인해보면 된다.

로그인 성공 후 Cookies 탭에서 SESSIONID 쿠키가 있는지 확인해야 다음 단계로 넘어갈 수 있다.


서버가 인증된 요청으로 인식하지 못하고 있음 → 요청이 올바르게 전달되지 않거나 Spring Security 설정에 문제가 있을 가능성이 커보임.

  1. 쿠키 확인.
    166330A9B780E340824FC845DA40C02B


참고로 이런 화살표가 뜨면 절대 안됌.
-> Cookie 헤더를 추가할 때 공백이나 특수문자가 들어가면 문제가 발생한다.


흠,, 뭐가 문제지 ?

가능한 원인 3가지

  1. JSESSIONID 쿠키가 요청과 함께 서버에 정상적으로 전달되지 않음 (Postman 설정 문제)
  2. Spring Security 설정에서 /api/schedules/** 접근을 차단하고 있음 (SecurityConfig 문제)
  3. Controller에서 session.getAttribute("user")가 null을 반환하고 있음 (세션 문제)

SecurityConfig

package com.example.scheduledemo.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())  // CSRF 비활성화
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))  // 세션 기반 인증 사용
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/users/signup", "/api/users/login").permitAll()  // 회원가입, 로그인은 인증 없이 허용
                        .anyRequest().authenticated())  // 나머지 요청은 인증 필요
                .formLogin(login -> login.disable())  // 기본 로그인 폼 비활성화
                .httpBasic(httpBasic -> httpBasic.disable());  // HTTP Basic 인증 비활성화

        return http.build();
    }
}

현재 문제점

  • SecurityConfig에서 "/api/users/signup""/api/users/login"permitAll() 처리했지만, /api/schedules/** 경로는 authenticated() 처리만 되어 있음.
  • CSRF 보호 비활성화(.csrf(csrf -> csrf.disable()))는 되어 있지만,
    POST 요청이 막혀있을 가능성이 있음.

해결책

  • 로그인한 사용자만 일정 생성 가능하도록 허용
  • POST 요청을 정상적으로 처리하도록 csrf().disable() 유지
  • Spring Boot가 SESSIONID(또는 JSESSIONID)를 유지하도록 설정
@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())  //CSRF 보호 비활성화 (POST 요청 차단 방지)
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))  //세션 기반 인증 유지
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/users/signup", "/api/users/login").permitAll()  //회원가입 & 로그인 허용
                        .requestMatchers("/api/schedules/**").authenticated()  //일정 관련 요청 인증 필요
                        .anyRequest().authenticated())  //모든 요청에 인증 필요
                .formLogin(login -> login.disable())  // 기본 로그인 폼 비활성화
                .httpBasic(httpBasic -> httpBasic.disable());  // HTTP Basic 인증 비활성화

        return http.build();
    }
  1. 기존 코드에서 큰 문제는 없음
    → authenticated() 설정은 맞음
  2. 추가 확인해야 할 사항
  • JSESSIONID가 요청에 정상적으로 포함되었는지 확인
  • POST 요청이 정상적으로 실행되었는지 로그 확인 필요

아직 문제해결되지 않음.


ScheduleController에서 문제 확인

현재 코드에서

createSchedule(@RequestParam Long userId, @RequestBody Schedule schedule)

  • 이 방식은 403 Forbidden을 유발할 가능성이 큼.
  • 로그인된 사용자의 session에서 유저 정보를 가져오지 않음.
  • 즉, userId를 요청에서 받지만, 실제 로그인한 사용자를 기반으로 일정을 생성하지 않음.

해결책

  • 세션에서 로그인된 사용자 정보를 가져와야 함.
  • HttpSession을 활용하여 사용자 인증을 확인.
  • 수정된 코드에서는 세션에서 user 객체를 꺼내 일정 생성.

ScheduleController

@PostMapping
    public ResponseEntity<Schedule> createSchedule(@RequestParam Long userId, @RequestBody Schedule schedule) {
        return ResponseEntity.ok(scheduleService.createSchedule(userId, schedule));
    }
    
// 수정 후
@PostMapping
    public ResponseEntity<?> createSchedule(@RequestBody Schedule schedule, HttpSession session) {
        //세션에서 로그인된 사용자 가져오기
        User user = (User) session.getAttribute("user");

        //사용자가 로그인되지 않은 경우 처리
        if (user == null) {
            return ResponseEntity.status(403).body("{\"message\": \"로그인이 필요합니다.\"}");
        }

        //로그인한 사용자의 ID를 기반으로 일정 생성
        schedule.setUser(user);
        Schedule savedSchedule = scheduleService.createSchedule(user.getId(), schedule);
        return ResponseEntity.ok(savedSchedule);
    }

변경된 점

  • @RequestParam userId 제거 → 세션에서 로그인한 사용자 정보를 가져오는 방식으로 변경
  • 로그인한 사용자가 없으면 403 응답 반환
  • schedule.setUser(user); → 일정을 로그인된 사용자에 연결
  • 일정 생성 시 user.getId() 기반으로 생성하도록 scheduleService.createSchedule(user.getId(), schedule); 호출


Postman에서 로그인 후 쿠키 확인시

SESSIONID가 아닌 JSESSIONID가 반환됨.
Spring Security에서 기본적으로 세션을 JSESSIONID로 관리함.
하지만 인증이 처리되지 않아 403이 발생.
즉, 세션이 제대로 유지되지 않고 있음.

SecurityConfig에서 Spring Security의 기본 사용자 생성

로그에서 확인해보니까, Using generated security password:라는 메시지가 보임.

Spring Security에서 기본적으로 inMemoryUserDetailsManager를 사용해서 인증을 수행하고 있음.
우리가 만든 세션 기반 인증(HttpSession)과 충돌할 가능성이 높음.
즉, 현재 로그인한 사용자가 SecurityContext에서 인식되지 않는 상태일 가능성이 큼.


문제해결

현재 SecurityConfig는 Spring Security의 UserDetailsService를 사용하지 않고 있음.

  • 하지만, Security가 자동으로 inMemoryUserDetailsManager를 활성화하는 것이 문제.
    Spring Security의 UserDetailsService를 명시적으로 사용 안함으로 설정해야 함.

SecurityConfig 코드 수정

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())  //CSRF 보호 비활성화 (POST 요청 차단 방지)
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))  //세션 기반 인증 유지
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/users/signup", "/api/users/login").permitAll()  //회원가입 & 로그인 허용
                        .requestMatchers("/api/schedules/**").authenticated()  //일정 관련 요청 인증 필요
                        .anyRequest().authenticated())  //모든 요청에 인증 필요
                .formLogin(login -> login.disable())  // 기본 로그인 폼 비활성화
                .httpBasic(httpBasic -> httpBasic.disable());  // HTTP Basic 인증 비활성화

        return http.build();
    }

에서

package com.example.scheduledemo.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())  
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))  
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/users/signup", "/api/users/login").permitAll()  
                .anyRequest().authenticated())  
            .formLogin(login -> login.disable())  
            .httpBasic(httpBasic -> httpBasic.disable())  /
            .logout(logout -> logout
                .logoutUrl("/api/users/logout") 
                .deleteCookies("JSESSIONID")  
                .invalidateHttpSession(true)); 

        return http.build();
    }
}

수정된 점

  1. inMemoryUserDetailsManager 자동 설정 방지 → Security의 기본 인증을 완전히 끔
  2. 로그아웃 시 JSESSIONID 삭제하여 새로운 로그인 시 세션 유지가 원활하도록 설정
  3. sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) 유지하여 세션이 필요할 때만 생성

ScheduleController 수정

찾아보니 현재 createSchedule에서 세션을 활용하는 방식이 부적절함.
-> 기존 HttpSession에서 user를 가져오고 있지만, SecurityContext에서 인증되지 않은 사용자는 403 Forbidden이 발생할 수 있음.

@PostMapping
    public ResponseEntity<?> createSchedule(@RequestBody Schedule schedule, HttpSession session) {
        User user = (User) session.getAttribute("user");

        if (user == null) {
            return ResponseEntity.status(403).body("{\"message\": \"로그인이 필요합니다.\"}");
        }

        schedule.setUser(user);
        Schedule savedSchedule = scheduleService.createSchedule(user.getId(), schedule);
        return ResponseEntity.ok(savedSchedule);
    }

수정된 코드

@PostMapping
    public ResponseEntity<?> createSchedule(@RequestBody Schedule schedule, HttpServletRequest request) {
        HttpSession session = request.getSession(false);  //기존 세션 가져오기

        if (session == null || session.getAttribute("user") == null) {
            return ResponseEntity.status(403).body("{\"message\": \"로그인이 필요합니다.\"}");
        }

        User user = (User) session.getAttribute("user");  //로그인된 사용자 정보 가져오기
        schedule.setUser(user);
        Schedule savedSchedule = scheduleService.createSchedule(user.getId(), schedule);

        return ResponseEntity.ok(savedSchedule);
    }

수정된 점

  1. HttpServletRequest request를 받아서 HttpSession session = request.getSession(false); 사용
    -> 기존 세션이 없는 경우 null 반환하여 403 Forbidden을 방지함
  2. session.getAttribute("user")에서 로그인된 사용자 정보를 가져와야 함
    user.getId()를 사용하여 일정을 로그인한 사용자와 연결

E7B6719B9D052070B1C5D0F1AF7305A4

E7B6719B9D052070B1C5D0F1AF7305A4

맞는지 3번 확인해주고,

헤더의 키 벨류에 넣어주고,
역시나 403 Forbidden이 뜬다.
내가 놓친 것은 무엇일까 ?


쉬고, 전체적인 코드를 훑어보면서 다시 생각해봤다.

현재 발생하는 문제점

무엇들이 있길래 해결되지 않은 것일까 해당 코드 부분에서만이 아니라 다른 곳들에서도 내가 놓치고 있는 부분들이 있을까 ?

📌 1. SecurityContext에 인증 정보 저장 안됨

현재 UserService.login()에서 SecurityContextHolder에 인증 정보 저장을 안 하고 있음.
session.setAttribute("user", user); 만으로는 Spring Security가 사용자를 인식하지 못함.
따라서, /api/schedules 엔드포인트 접근 시 403 Forbidden 발생하는중인 것 같음.

📌 2. UserRepository에서 findByEmail()이 Optional이 아닌 User 반환

UserRepository.findByEmail(email)에서 null을 반환할 가능성이 있음.
Optional<User> userOptional = userRepository.findByEmail(email);로 변경하면 더 안전함.

📌 3. SecurityFilterChain에서 세션 정책 미설정

sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)를 사용했지만, 기본 인증 필터를 설정하지 않음.
세션을 통한 인증을 명확하게 설정해야 함.

📌 4. ScheduleController에서 SecurityContextHolder 미사용

현재 ScheduleController.createSchedule()에서 세션에서만 사용자 정보를 가져옴.
SecurityContextHolder.getContext().getAuthentication()에서 인증 정보를 가져오는 게 더 적절함.


해결 방법

  1. UserService에서 SecurityContextHolder에 사용자 정보 저장하기
  2. UserRepository에서 Optional로 반환하도록 수정
  3. SecurityConfig에서 세션 인증을 명확하게 유지하도록 변경
  4. ScheduleController에서 SecurityContextHolder에서 사용자 가져오기

1. UserService 수정 (SecurityContextHolder 설정 추가)

public boolean login(String email, String password, HttpServletRequest request, HttpServletResponse response) {
        User user = userRepository.findByEmail(email);

        if (user != null && passwordEncoder.matches(password, user.getPassword())) {
            // 세션 생성 및 저장
            HttpSession session = request.getSession();
            session.setAttribute("user", user);
            return true;
        }
        return false;
    }

에서

public boolean login(String email, String password, HttpServletRequest request, HttpServletResponse response) {
        Optional<User> userOptional = userRepository.findByEmail(email);

        if (userOptional.isPresent()) {
            User user = userOptional.get();
            if (passwordEncoder.matches(password, user.getPassword())) {
                HttpSession session = request.getSession(); // 세션 활용
                session.setAttribute("user", user);

                //SecurityContextHolder에도 저장
                SecurityContext securityContext = SecurityContextHolder.getContext();
                securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList()));

                return true;
            }
        }
        return false;
    }
  1. SecurityContextHolder.getContext().setAuthentication(...)추가
  2. findByEmail(email)을 Optional<User>로 변경하여 안전성 강화
    이제 Spring Security가 사용자를 인식할 수 있도록 바뀐거임.

이해하기 쉽게 다시 설명하면,
🔍 이전 코드의 문제점
이전 코드에서 UserService.login() 메서드는 사용자를 검증한 후, 세션(Session)에만 사용자 정보를 저장하고 있었습니다.

public boolean login(String email, String password, HttpServletRequest request, HttpServletResponse response) {
    User user = userRepository.findByEmail(email);

    if (user != null && passwordEncoder.matches(password, user.getPassword())) {
        // 문제: Spring Security가 이 정보를 인식하지 못함
        HttpSession session = request.getSession();
        session.setAttribute("user", user); // 세션에만 사용자 저장
        return true;
    }
    return false;
}

문제점

  1. session.setAttribute("user", user); → Spring Security가 관리하는 인증(Authentication) 정보가 아님

  2. SecurityContextHolder에 사용자 정보가 저장되지 않음

  3. Spring Security의 인증 필터가 요청을 처리할 때 "로그인된 사용자가 없음"으로 판단
    → 그래서 인증이 필요한 API 요청(/api/schedules 등)이 403 Forbidden을 반환함.

    해결 방법: SecurityContextHolder에 사용자 정보 저장
    수정 코드 (이제 Spring Security가 사용자를 인식함)

public boolean login(String email, String password, HttpServletRequest request, HttpServletResponse response) {
    Optional<User> userOptional = userRepository.findByEmail(email);

    if (userOptional.isPresent()) {
        User user = userOptional.get();
        if (passwordEncoder.matches(password, user.getPassword())) {
            // 기존 방식: 세션에 사용자 저장
            HttpSession session = request.getSession();
            session.setAttribute("user", user);

            // 추가한 부분: SecurityContextHolder에도 사용자 저장
            SecurityContext securityContext = SecurityContextHolder.getContext();
            securityContext.setAuthentication(
                new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList())
            );

            return true;
        }
    }
    return false;
}

추가된 핵심 코드

SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(
    new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList())
);

왜 이게 해결책이 되는가?

  1. Spring Security는 SecurityContextHolder.getContext()를 통해 현재 로그인된 사용자를 확인함.
    • SecurityContextHolder.getContext().getAuthentication()이 null이면 로그인되지 않은 것으로 간주.
    • 따라서 반드시 securityContext.setAuthentication(...)을 호출해야 Spring Security가 로그인 상태를 유지할 수 있음.
  2. 인증(Authentication) 객체를 설정해줌으로써, SecurityContext가 사용자를 인식할 수 있음.
    • UsernamePasswordAuthenticationToken(user, null, Collections.emptyList()) → 사용자 정보를 포함하는 인증 토큰을 생성하여 SecurityContext에 저장.
  3. 이제 인증이 필요한 API에서도 사용자 정보를 인식할 수 있음.
    • SecurityContextHolder.getContext().getAuthentication().getPrincipal()로 사용자를 가져올 수 있음.
    • 따라서, /api/schedules 같은 인증이 필요한 엔드포인트에서도 403 Forbidden이 발생하지 않음.

다시 본론으로 돌아가,

2. UserRepository 수정 (Optional 반환)

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

3. ScheduleController 수정 (SecurityContextHolder에서 사용자 가져오기)

 @PostMapping
    public ResponseEntity<?> createSchedule(@RequestBody Schedule schedule, HttpServletRequest request) {
        HttpSession session = request.getSession(false);  //기존 세션 가져오기

        if (session == null || session.getAttribute("user") == null) {
            return ResponseEntity.status(403).body("{\"message\": \"로그인이 필요합니다.\"}");
        }

        User user = (User) session.getAttribute("user");  //로그인된 사용자 정보 가져오기
        schedule.setUser(user);
        Schedule savedSchedule = scheduleService.createSchedule(user.getId(), schedule);

        return ResponseEntity.ok(savedSchedule);
    }

에서

@PostMapping
    public ResponseEntity<?> createSchedule(@RequestBody Schedule schedule) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || authentication.getPrincipal() == null || !(authentication.getPrincipal() instanceof User)) {
            return ResponseEntity.status(403).body("{\"message\": \"로그인이 필요합니다.\"}");
        }

        User user = (User) authentication.getPrincipal();
        schedule.setUser(user);
        Schedule savedSchedule = scheduleService.createSchedule(user.getId(), schedule);

        return ResponseEntity.ok(savedSchedule);
    }

로 수정
이전 방식은 세션만 사용해서 너무 잘못된 방식이라 생각 했으며,
session.getAttribute("user") → Spring Security가 관리하는 방식이 아님
로그인은 되었어도 SecurityContext에 정보가 없으면 403 Forbidden 발생
따라서, SecurityContextHolder 활용해봤습니다.

  1. 세션이 아니라 SecurityContextHolder.getContext()를 통해 로그인된 사용자 확인
  2. authentication.isAuthenticated()로 인증 상태를 정확히 체크
  3. Spring Security가 관리하는 인증 객체에서 사용자 정보 가져옴 (getPrincipal())
  4. 이제 403 Forbidden 오류 없이 정상 작동 가능합니다.

의존성 추가


git fetch origin
git checkout main
git pull origin main
git checkout feat
git rebase main
git push origin feat --force


네 이러면 다 날라가니까 여러분들은 꼭 주의하세요 ! ㅎㅎ..

깃에 관련해서 참고 할만한 링크

또 똑같이 에러.
그냥 rebase 등 깃헙 관련해서 확실하게 공부하는 것이 좋겠음을 느꼇다.

겨우 살렸는데,

에러 메시지

There isn’t anything to compare. main and feat are entirely different commit histories.

❓ 원인

main과 feat 브랜치가 완전히 독립적인 커밋 히스토리를 가지고 있음
즉, feat 브랜치는 main에서 파생된 게 아니라 완전히 별개로 존재하는 상태
Git은 두 브랜치를 비교할 수 없어서 PR을 만들 수 없음


해결 방법

feat 브랜치를 main 위에 정리하기

현재 해결 방법은 feat 브랜치를 main에서 파생된 것으로 정리하는 것이며,
이제 feat을 main 위에 올린 후 다시 GitHub에 푸시하면 PR이 가능해질 것 같은데..?

1. feat 브랜치로 이동

git checkout feat

2. feat 브랜치를 main 기반으로 rebase

git rebase main
이 과정에서 충돌(conflict)이 발생할 수도 있음.

만약 충돌이 발생하면
1. git status로 충돌 난 파일 확인
2. 해당 파일을 수정 후 git add 충돌해결된파일
3. git rebase --continue
4. 충돌이 발생하지 않을 때까지 반복

3. 변경 사항을 강제로 원격 저장소에 푸시

git push origin feat --force

주의: --force를 사용하면 기존 원격 feat 브랜치의 히스토리가 덮어씌워지므로, 꼭 확인하고 실행해야 함!

4. GitHub에서 다시 PR 생성 시도

이제 GitHub에서 feat → main으로 PR을 만들면 정상적으로 비교가 가능할


작업을 새벽에 마무리 했으나, pr이 안 돼서,

1. 원격(main)과 로컬(feat)의 관계가 완전히 끊어진 상태.

  • 원격(origin/main)을 가져오지 않은 상태에서 feat을 작업했기 때문이라고 판단했고,
    git checkout -b main origin/main을 하기 전에 이미 main 브랜치가 있어서 오류가 발생했습니다.
    그 상태에서 git reset --hard main을 실행하면서, feat 브랜치의 작업이 초기화 되었습니다.

🚨 git reset --hard main이 위험했던 이유
git reset --hard main
이 명령어는 feat 브랜치를 main의 상태로 강제 덮어씌우는 명령.
하지만, main 브랜치에는 아무 코드도 없었음.
결과적으로 feat 브랜치의 코드가 완전히 날아가 버리게 된 것입니다! 😱

2. git merge --allow-unrelated-histories 사용했지만, 필요 없었음

git merge --allow-unrelated-histories
이 옵션은 서로 다른 커밋 히스토리를 가진 두 브랜치를 병합할 때 사용하는 것임에도 불구하고,
이미 fetch & rebase를 하려던 상황에서는 필요 없었음.
이걸 실행해도 변경된 게 없어서 "이미 업데이트 상태입니다."가 나왔던 것입니다.

3. git push origin feat --force로 강제 덮어씌운 게 문제

결국 돌고 돌아 해당 명령어를 통해 '원격 브랜치를 강제로 덮어씌우는' 행위를 했습니다.
즉, reset --hard main으로 날려버린 상태를 그대로 github에 업로드한 것이다.
그래서, github에도 코드가 전부 사라졋던 것이다.

정리) 즉, 왜 PR이 안 됐을까?

feat이 main과 연결되지 않은 상태가 되었기 때문
PR을 만들려면 feat 브랜치가 main을 기반으로 만들어져야 함.
하지만 git reset --hard main으로 feat과 main의 연결을 끊어버려서 비교할 수 없게 된 것.


그렇다면, 앞으로 어떻게 해야 할 것인가? (개선점 및 해결책)
이제 같은 실수를 반복하지 않도록 안전한 방법을 정리해보겠습니다.

앞으로 안전하게 PR을 생성하는 법

🔥 git reset --hard는 웬만하면 사용하지 말기!
🌟 항상 fetch 후 rebase를 사용해서 브랜치 정리하기!

//GitHub 원격 브랜치 가져오기
git fetch origin

// 최신 main 브랜치를 가져오기
git checkout main
git pull origin main

//최신 main을 기반으로 feat 브랜치 정리하기
git checkout feat
git rebase main  # 여기서 충돌 나면 해결 후 git rebase --continue

//안전하게 원격에 업로드
git push origin feat --force

결론

  1. git reset --hard는 정말 필요할 때만 사용하는 것이 좋다.
    → reset --hard main을 해버려서 feat 브랜치 작업이 다 날아갔던 것.

  2. 항상 git fetch origin & git rebase main을 먼저 하기!
    → feat이 main과 연결된 상태로 유지돼야 PR을 만들 수 있음.

  3. GitHub에 push할 때는 --force를 주의해서 사용!
    → 실수로 날린 코드가 있다면 git reflog로 복구할 수도 있음.

profile
백엔드를 지향하며, 컴퓨터공학과를 졸업한 취준생입니다. 많이 부족하지만 열심히 노력해서 실력을 갈고 닦겠습니다. 부족하고 틀린 부분이 있을 수도 있지만 이쁘게 봐주시면 감사하겠습니다. 틀린 부분은 댓글 남겨주시면 제가 따로 학습 및 자료를 찾아봐서 제 것으로 만들도록 하겠습니다. 귀중한 시간 방문해주셔서 감사합니다.

0개의 댓글