Git?

Git은 Linux 운영 체제 커널을 만든 사람으로 유명한 Linus Torvalds가 개발하였다. 

리눅스 환경에서 버전관리를 위해 만들어진 분산 버전 관리 시스템이다.

 

버전 관리?

팀프로젝트 마지막 발표를 위한 ppt를 만든다고 가정하자.

1. 초안 PPT 작성 후 팀원들에게 보여준다. (발표.ppt)

2. 팀원의 피드백으로 수정을 한다. (발표2.ppt)

3. 다시 피드백을 받고, 수정한다. (발표3.ppt)

이러한 피드백과 수정 절차가 계속 반복되고 나면 어느새 발표n.ppt가 되어 있다.

이렇게 계속되서 새로운 버전의 ppt 파일이 쌓이다 보면 어떤 버전에서 어느 부분이 수정되고, 삭제되었는지도 현재 버전이 몇 버전인지도 헷갈리게 된다.

이러한 버전 관리를 수동이 아닌 자동으로 관리해주는 것이 버전 관리 시스템이다.

버전 관리 시스템에는 여러가지가 있지만 그중 대표적으로 가장 많이 사용되는 것이 Git이다.

 

버전 관리 시스템의 목적

1. version

2. backup

3. cooperation

 

Git 이해하기

Git의 가장 기본적으로 많이 사용하는 명령어는 아래 5가지가 있다.

1. commit - 변경 사항을 저장

2. push - 로컬에 저장해 놓은 변경 사항들을 원격저장소에 저장

3. pull - 원견 저장소에 저장된 데이터를 로컬로 받아옴

4. branch - 새로운 브랜치 생성

5. merge - 브랜치와 브랜치 병합(결합)

 

1. Commit 

Commit은 변경된 내용을 저장하는 것이다.

Commit을 하게 되면 아래 사진과 같이 누가, 언제, 어떤 브랜치로 커밋을 했는지 알 수 있다.

CommitID의 경우 Author + Date + Commit Message 를 하나의 문자열로 만들어 SHA-1 해시함에 돌려 만든 값의 40자를 commit id로 사용한다.

git commit -m "commit message"

 

만약 내가 test.txt 파일에 1을 입력하고 v1으로 commit을 하면 main branch로 v1이 commit 된 것을 확인할 수 있다.

그리고 로컬에서 다시 test.txt파일에 1을 지우고 2를 입력하고 v2으로 commit하면 main branch로 v2가 commit된 것을 확인할 수 있다.

 

2.  branch

branch는 보통 새로운 기능을 추가하거나 할때 사용한다. 

git branch feature/login

 

3. push

Push는 원격 저장소에 로컬에서 내가 commit한 내용들을 저장하는 것이다.

# git push {원격저장소 이름} {원격저장소에 저장할 브랜치 이름}
git push origin main

 

4. pull

Pull은 원격 저장소에서 로컬로 저장된 내용들을 받아오는 것이다.

# git pull {원격저장소 이름} {받아올 branch 이름}
git pull origin main

 

 

4.  merge

merge는 원격 저장소에서 pull 받을 때 conflict가 나거나 작업중이던 branch를 다른 branch와 합치고자 할 때 사용한다.

'git merge {target branch 이름}' 명령어를 실행하게 되면 현재 branch에 target branch가 병합되게 된다.

git merge {합칠 대상}

 

 

명령어 실습

1. test.txt 파일 생성 (내용 1)

 

2. commit message [v1]로 commit

- git init 은 해당 폴더가 git 버전 관리 대상 폴더라고 지정해주는 것

- git add -> commit 대상 파일로 지정

 

3. git show 명령어를 통해 현재 commit 현황 확인

HEAD -> main -> v1인 상태이다.

 

4. test.txt 파일 내용을 변경 후 v2로 commit

 

5. git show를 통해 현황 확인

HEAD -> main -> v2인 것을 확인

 

6. 현재 브랜치에서 새로운 브랜치 feature/login 생성

branch를 생성하고 checkout을 해줘야한다.

브랜치를 생성했다고 자동으로 해당 브랜치로 checkout되는 것이 아니다.

git branch feature/login
git checkout feature/login

 

7. login branch에서 login txt 파일 생성 후 login commit

 

8. git graph는 다음과 같아 진다.

HEAD -> feature/login -> login -> v2

main -> v2 -> v1

 

9. main으로 다시 checkout하고. [git checkout main ] v3를 commit 한다.

이렇게 되면 다시 HEAD -> main -> v3 -> v2 -> v1

feature/login -> login -> v2 가 된다.

그림으로 보면 다음과 같다.

login branch에서 login.txt 파일을 생성했기 때문에 v3에서는 login.txt 파일을 찾을 수 없는 상태가 된다.

login branch에서 작업한 내용을 모두 완료했다고 가정하여 main branch에 반영하고 싶을 때 merge를 사용한다.

 

10. login branch 작업 내용을 main branch에 merge

 

main에 feature/login을 merge하게 되면 아래와 같이 login.txt 파일을 확인할 수 있는 것을 볼 수 있다.

 

작업 내용은 다음과 같이 된다.

728x90

'DevOps\etc' 카테고리의 다른 글

[ CI/CD ] CI/CD란?  (4) 2022.11.10

Cookie가 뭐야?

Cookie는 HTTP의 비연결성, 무상태성을 보완하기 위한 방법 중 하나로, 서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각을 말한다. 서버가 보내준 조각(쿠키)를 브라우저가 저장을 해 놓았다가 동일한 서버에게 다시 요청을 보낼 때 조각(쿠키)와 함께 전송한다.

쿠키를 통해 동일한 사용자가 요청한 것인지 아닌지를 판단할 때 사용한다. 

 

* HTTP의 비연결성

클라이언트가 서버에 요청했을 때, 그 요청에 맞는 응답을 보낸 후 연결을 끊는 처리 방식

 

* 무상태성

클라이언트와 첫 번째 통신에서 데이터를 주고받았다 해도, 두 번째 통신에서는 이전 데이터를 유지하지 않는다.

 

✅ HTTP의 비연결성, 무상태성을 보완하기 위해 CookieSession이 사용된다.

 

 Cookie의 형태

쿠키는 <Key, Value> 형태로 구성된 String으로 4KB이상 저장할 수 없다.

보통 브라우저에 의해 저장되며,  Request들의 Cookie HTTP 헤더 안에 포함되어 전송된다.

또한 브라우저마다 저장되는 쿠키가 다르다, 크롬에서 로그인해서 받은 쿠키는 파이어폭스에서 사용할 수 없다.

 

Cookie를 이용한 로그인 구현

  1. 로그인 성공 시 서버가 사용자에게 쿠키를 넘겨줌.
  2. 브라우저가 쿠키를 저장
  3. 사용자가 서버에 다음 요청을 할때마다 쿠기도 함께 서버에게 보냄.
  4. 서버는 사용자 요청에 담겨온 쿠키를 통해 로그인 했는지, 유저 정보, 권한 등을 확인할 수 있음.

 

쿠키 설정 방법

// 쿠키 설정
Cookie cookie = new Cookie([cookie-name], [cookie-value]);

// 쿠키 유효기간 설정
cookie.setMaxAge(60 * 60); // 60초 * 60번 == 1시간

// 쿠키를 응답 헤더에 추가
response.addCookie(cookie); // response는 HttpServletResponse 객체이다.

 

로그인 기능 목록

  • 회원가입
    • 아이디 중복 체크 
    • password == passwordCheck 체크 
    • nickName 중복 체크
  • 로그인
    • login password 체크
  • 로그아웃

 

1. SpringBoot Initializr

https://start.spring.io/

 

dependency

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.mysql:mysql-connector-j'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

 

 

2. Member Entity 생성

package com.millpre.cookielogin.member.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Member {
    @Id
    @NotNull
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    private String loginId;

    @NotNull
    private String password;

    @NotNull
    private String nickName;
}

 

3. MemberRepository 생성

package com.millpre.cookielogin.member.entity.repository;

import com.millpre.cookielogin.member.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, Long> {
    boolean existsByLoginId(String loginId); // 존재하는 loginId인지 확인
    boolean existsByNickName(String nickName); // 존재하는 닉네임인지 확인
    Optional<Member> findByLoginId(String loginId); // loginId로 Member찾기
}

 

4. JoinDto 생성

package com.millpre.cookielogin.member.entity.dto;

import com.millpre.cookielogin.member.entity.Member;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class JoinDto {
    @NotBlank(message = "로그인 아이디가 비어있습니다.")
    private String loginId;
    @NotBlank(message = "비밀번호가 비어있습니다.")
    private String password;
    private String passwordCheck;
    @NotBlank(message = "닉네임이 비어있습니다.")
    private String nickName;

    public Member toEntity() {
        return Member.builder()
                .loginId(this.loginId)
                .nickName(this.nickName)
                .password(this.password)
                .build();
    }
}

 

5. LoginDto 생성

package com.millpre.cookielogin.member.entity.dto;

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

@Getter
@Setter
@NoArgsConstructor
public class LoginDto {
    private String loginId;
    private String password;
}

 

6. MemberService 생성

package com.cookie.login.service;

import com.cookie.login.domain.dto.JoinRequest;
import com.cookie.login.domain.dto.LoginRequest;
import com.cookie.login.domain.entity.User;
import com.cookie.login.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@Service
@Transactional
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;

    /* loginId 중복 체크
    * 중복O true
    * 중복X false
    * */
    public boolean checkLoginIdDuplicate(String loginId) {
        return userRepository.existsByLoginId(loginId);
    }

    /* nickName 중복 체크
    * 중복O true
    * 중복X false
    * */
    public boolean checkNickNameDuplicate(String nickName) {
        return userRepository.existsByNickName(nickName);
    }

    /* 회원 가입 기능
    * loginId, nickName 중복 체크는 Controller 에서 => 에러 메시지 출력을 위해서
    * */
    public void join(JoinRequest request) {
        userRepository.save(request.toEntity());
    }

    /* 로그인 기능
    * 로그인 아이디가 존재하지 않거나 비밀번호가 일치하지 않는 경우 null return */
    public User login(LoginRequest request) {
        Optional<User> optionalUser = userRepository.findByLoginId(request.getLoginId());

        // loginId가 일치하는 User가 없으면 null return
        if (optionalUser.isEmpty()) return null;

        User user = optionalUser.get();

        // 찾아온 User의 password와 입력된 password가 다르면 null return
        if (!user.getPassword().equals(request.getPassword())) return null;

        return user;
    }

    /* userId(Long)를 입력받아 User return 해주는 기능
    * 인증, 인가 시 사용
    * userId가 DB에 없거나, null이면 null return*/
    public User getLoginUserById(Long userId) {
        if (userId == null) return null;
        Optional<User> optionalUser = userRepository.findById(userId);
        if(optionalUser.isEmpty()) return null;
        return optionalUser.get();
    }
}

 

7. MemberController 생성

package com.cookie.login.controller;

import com.cookie.login.domain.UserRole;
import com.cookie.login.domain.dto.JoinRequest;
import com.cookie.login.domain.dto.LoginRequest;
import com.cookie.login.domain.entity.User;
import com.cookie.login.service.UserService;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;

@Controller
@RequiredArgsConstructor
@RequestMapping("/cookie-login")
public class HomeController {
    private final UserService userService;
    @GetMapping(value = {"/", ""})
    public String home(@CookieValue(name="userId", required = false) Long userId,
                       Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        User loginUser = userService.getLoginUserById(userId);
        System.out.println(loginUser);

        if (loginUser != null) {
            System.out.println(loginUser.getNickName());
            model.addAttribute("nickname", loginUser.getNickName());
        }
        return "home";
    }

    @GetMapping("/join")
    public String joinPage(Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        model.addAttribute("joinRequest", new JoinRequest());
        return "join";
    }

    @GetMapping("/info")
    public String userInfo(@CookieValue(name = "userId", required = false) Long userId, Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        User loginUser = userService.getLoginUserById(userId);

        if(loginUser == null) {
            return "redirect:/cookie-login/login";
        }

        model.addAttribute("loginId", loginUser.getLoginId());
        model.addAttribute("nickname", loginUser.getNickName());
        model.addAttribute("role", loginUser.getRole());

        return "info";
    }

    @GetMapping("/admin")
    public String adminPage(@CookieValue(name = "userId", required = false) Long userId, Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        User loginUser = userService.getLoginUserById(userId);

        if (loginUser == null) {
            return "redirect:/cookie-login/login";
        }

        if (!loginUser.getRole().equals(UserRole.ADMIN)) {
            return "redirect:/cookie-login";
        }
        return "admin";
    }

    @GetMapping("/login")
    public String login(Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        model.addAttribute("loginRequest", new LoginRequest());
        return "login";
    }


    @PostMapping("/login")
    public String login(@ModelAttribute LoginRequest loginRequest,
                        HttpServletResponse response,
                        BindingResult bindingResult,
                        Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        User user = userService.login(loginRequest);

        if (user == null) {
            bindingResult.reject("loginFail", "로그인 아이디 또는 비밀번호가 틀렸습니다.");
            return "login";
        }

        // 로그인 성공
        Cookie cookie = new Cookie("userId", String.valueOf(user.getId()));
        cookie.setMaxAge(60 * 60); // 쿠키 유효 시간: 1시간
        response.addCookie(cookie);

        return "redirect:/cookie-login";
    }


    @PostMapping("/join")
    public String join(@Valid @RequestBody JoinRequest joinRequest, BindingResult bindingResult, Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        // loginId 중복 확인
        if (userService.checkLoginIdDuplicate(joinRequest.getLoginId())) {
            bindingResult.addError(new FieldError("joinRequest", "loginId", "로그인 아이디가 중복됩니다."));
        }

        // 닉네임 중복 체크
        if (userService.checkNickNameDuplicate(joinRequest.getNickName())) {
            bindingResult.addError(new FieldError("joinRequest", "nickname", "닉네임이 중복됩니다."));
        }

        // password와 passwordCheck가 같은지 체크
        if (!joinRequest.getPassword().equals(joinRequest.getPasswordCheck())) {
            bindingResult.addError(new FieldError("joinRequest", "passwordCheck", "비밀번호가 일치하지 않습니다."));
        }

        if (bindingResult.hasErrors()) {
            return "join";
        }

        userService.join(joinRequest);
        return "redirect:/cookie-login";
    }

    @GetMapping("/logout")
    public String logout(HttpServletResponse response, Model model) {
        model.addAttribute("loginType", "cookie-login");
        model.addAttribute("pageName", "쿠키 로그인");

        Cookie cookie = new Cookie("userId", null);
        cookie.setMaxAge(0);
        response.addCookie(cookie);
        return "redirect:/cookie-login";
    }
}

 

참고

 

[Spring Boot] Cookie를 사용한 로그인 구현

쿠키(Cookie) 란? 쿠키 : 사용자가 웹사이트 접속시 사용자의 개인 장치에 다운로드되고 브라우저에 저장되는 작은 텍스트 파일 웹사이트는 이 쿠키를 이용해 사용자의 장치를 인식하고 일부 데이

chb2005.tistory.com

728x90

블로그

https://www.acmicpc.net/problem/21921

 

21921번: 블로그

첫째 줄에 $X$일 동안 가장 많이 들어온 방문자 수를 출력한다. 만약 최대 방문자 수가 0명이라면 SAD를 출력한다. 만약 최대 방문자 수가 0명이 아닌 경우 둘째 줄에 기간이 몇 개 있는지 출력한다

www.acmicpc.net

 

내 코드

시간초과 코드

시간초과 났던 이유

- 인덱싱 + sum 함수사용

- 처음 0~X일까지의 구간합을 구한 다음 거기에 다음 인덱스에서 필요한 값을 빼고, 더해주는 방식으로 수정

from sys import stdin

N, X = map(int, stdin.readline().split())
visited = list(map(int, stdin.readline().split()))

maxVisit = 0
maxVisitCount = 0
for i in range(N - X + 1):
    sliceVisit = visited[i: i+X]
    if maxVisit < sum(sliceVisit):
        maxVisit = sum(sliceVisit)
        maxVisitCount = 1
    elif maxVisit == sum(sliceVisit):
        maxVisitCount += 1
    else:
        pass

if maxVisit == 0:
    print("SAD")
else:
    print(maxVisit)
    print(maxVisitCount)

 

성공 코드

from sys import stdin

N, X = map(int, stdin.readline().split())
visited = list(map(int, stdin.readline().split()))


intervalSum = sum(visited[0: X])
maxVisit = intervalSum
maxVisitCount = 1

for i in range(1, N - X + 1):
    intervalSum = intervalSum - visited[i-1] + visited[i+X-1]
    if intervalSum > maxVisit:
        maxVisit = intervalSum
        maxVisitCount = 1
    elif intervalSum == maxVisit:
        maxVisitCount += 1

if maxVisit == 0:
    print("SAD")
else:
    print(maxVisit)
    print(maxVisitCount)
728x90

영단어 암기는 괴로워

 

20920번: 영단어 암기는 괴로워

첫째 줄에는 영어 지문에 나오는 단어의 개수 $N$과 외울 단어의 길이 기준이 되는 $M$이 공백으로 구분되어 주어진다. ($1 \leq N \leq 100\,000$, $1 \leq M \leq 10$) 둘째 줄부터 $N+1$번째 줄까지 외울 단

www.acmicpc.net

 

내 코드

from sys import stdin

N, M = map(int, stdin.readline().split())
# print(N, M)
dicList = []
dic = {}


for _ in range(N):
    word = stdin.readline().strip()
    if len(word) >= M:
        dicList.append(word)
# print(dicList)

for word in dicList:
    if not word in dic:
        dic[word] = 1
    else:
        dic[word] += 1

keys = dic.keys()
# print(dic)
dic = sorted(dic.items(), key=lambda x: (-x[1], -len(x[0]), x[0]))
for word, count in dic:
    print(word)
728x90

+ Recent posts