개괄

드디어 주특기 주차로 접어들었다. 가장 먼저 한 것은 JPA를 활용해 웹종때 수강했던 간단한 post를 CRUD하는 subject를 spring을 이용해 재 구현해 보았다. 그리고는 Spring Boot Framework의 원리에 대해 수강했다.

 

목표

 

배운것 - TIL SERIES 

스프링 프레임워크의 간단 정리

 

스프링 프레임워크 시리즈

 

왜?라는 물음은 왜 중요할까?

 

개괄

이번 개인 과제는 JPA를 사용해서 게시판을 만들어보는 과정이었다. 무슨 문제가 있었는지 확인해보고, 해결해보자.

사실은 게시판이라기보다는 거의 뭐 쪽지 짬통 테스트같은 느낌이다.

 

github

https://github.com/choincnp/SpringBoard

 

GitHub - choincnp/SpringBoard

Contribute to choincnp/SpringBoard development by creating an account on GitHub.

github.com

 

설계 방식

1. 요구사항 받아들이기

2. API 명세서 설계하기

3. 기능명세서 설계하기

4. 디렉토리 분리하기

 

개발 flow

1. entity를 먼저 만든다.

2. 그 entity를 담을 Repository를 만든다.

3. 2번에서 만든 Repository를 Controll할 Controller를 만든다.

4. Controller에서 받을 RequestDto를 만들기

5. 이제 받을 RequestDto를 핵심 로직으로 조질 Service를 설계한다.

6. 핵심 로직을 거쳐서 return해줄 ResponseDto를 설계한다.

 

여기서 문제 : 왜 Entity를 Dto에 담아서 return해줘야 하는가?

간단히 말하면, Entity의 모든 정보를 담아서 return해줄 필요는 없기 때문이다.

Board의 Entity 안에는 많은 정보가 들어간다. PK인 id값을 포함해 제목, 내용, 작성자 등도 들어가지만, 가장 중요한 게시물을 지울 수 있는 password값이 들어가있다. password값까지 response로 리턴해줄 필요도 없고, 또한 누가 접근했을 때 한단계 더 숨겨줄 수 있기 때문이다.(Encapsulism과 비슷한 맥락이라고 보면 된다.)

이 Letter를 list로 만들어서 반환하는 것이 아니라, 

이렇게 ResponseDto에 담아 주는것이 현명하다.

그럼 Letter를 ResponseDto로 바꿔 주어야 하는데, 어떻게, 또 어느 Layer에서 변환을 해 주어야 할까?

Controller Layer에서 변환해주어도 괜찮지만, 보통은 Service Layer에서 변환작업을 해 주는게 좋기도 하고, flow에 맞다.

 

그럼 Entity를 DTO로 어떻게 바꿔줄까?

내가 알아본 방법은 두 가지였다.

1. Stream을 이용해 바꿔주기

2. Array로 만들고 List로 바꿔주기

그 중 Stream을 이용하기로 했다.

이렇게 자바8의 collection을 사용해서 바꾸어 주었다.

LetterController

@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class MainController {

    private final LetterService letterService;

    @GetMapping("/posts")
    public List<LetterResponseDto> viewAll(){
        return letterService.findAll();
    }

    @PostMapping("/post")
    public Long postLetter(@RequestBody LetterRequestDto letterRequestDto){
        return letterService.postLetter(letterRequestDto);
    }

}

여기서 @RestController란 무엇일까?

@RestController = @Controller + @ResponseBody이다.

@Controller 어노테이션은 기존의 컨트롤러 어노테이션과 똑같고,

@ResponseBody 어노테이션은 return해주는 객체를 MessageConverter를 통해 매핑해서 본문에 담아 클라이언트에 전달한다.

 

처음에는 RequestBody와 헷갈려서 그대로 전달했다가 에러가 나버렸다.

그래서 Controller에 @RequestBody를 다 붙여주었다.

그랬더니 또 문제가 났다.

api 명세서에는 password만 들고 들어와야 하는 것이다.

보통 우리가 게시판에서 글을 지우려면 pop-up창에 password 하나만 치면 삭제가 되는 구조를 많이 이용한다.

그래서 이 password값만 받아오고 싶은데, 도저히 RequestDto에 있는 password값을 가져와서 다시 확인하기는 싫었다.

나중에 상세 페이지에 들어가서 삭제를 누르려면 RequestDto를 이용하면 되겠지만, 상세 페이지 밖에서 글을 삭제하려면

RequestDto에 있는 타이틀, 제목같은 not null값을 다 채워줘야 하기 때문이다.

 

한참을 500에러와 씨름하다가 다음과 같은 깨달음을 얻었다.

@RequestBody

RequestBody는 요청값을 json type로 받겠다고 선언하는 어노테이션으로, 이 어노테이션 덕분에 우리는 DTO 형태로 값을 가져올 수 있다. Spring은 Controller로 요청을 받을 때 jackson이라는 내장 라이브러리에 있는 MappingJackon2HttpMessageConverter를 이용해 요청값으로 들어온 json형태의 데이터를 DTO에 맞는 형태로 파싱해주게 된다. 그래서 우리는 스프링을 통해 json 형태를 편리하게 DTO라는 객체로 받아올 수 있는 것이다.

그래서 이 JSON 형태의 글이 @Responsebody 어노테이션을 통해

RequestDto requestDto = new RequestDto(title,content,author,password)

형태로 자동 바인딩되어 저장된다.

이를 "역직렬화 / DeSerialization"이라고 부른다! (Singleton패턴에서 왜 이런 개사기스킬을 만들어놨는지 궁금했는데 여기서 깨달았다.)

역직렬화는 생성자를 거치지 않고, Java의 Reflection을 통해 객체를 정의하는 매커니즘이다. 직렬화 가능한 클래스들은 기본 생성자가 항상 있어야 하기 때문에 RequestDto에 기본 생성자가 정의되어있지 않으면 데이터 바인딩에 실패한다.

 

아무튼 나는

@DeleteMapping("/post/{id}") //RequestBody 방식으로 구현, body : raw - text
    public String deleteLetter(@PathVariable Long id, @RequestBody String password) throws JSONException {
        return letterService.deleteLetter(id, password);
    }

이렇게 RequestBody 어노테이션 뒤에 String password를 선언하였기 때문에 JSON타입의 password가 내가 생각한 JSON객체로 들어오지않고

String password = “{\“password\“:\“1234\“}”;

와 같은 형태로 들어왔던 것이다.

 

그럼 어떻게 하는데!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

방법은 3개가 있다.

1. 죽어도 RequestBody를 쓰고 싶으면 @RequestBody JSONObject password로 받기

2. @ModelAttribute를 쓰기 : ModelAttribute는 RequestBody나 RequestParam을 선언하지 않으면 디폴트로 들어가기 때문에

    @DeleteMapping("/post/{id}") //ModelAttribute 방식으로 구현, body : x-www.form-urlencoded
    public String deleteLetter(@PathVariable Long id, String password) throws JSONException {
        return letterService.deleteLetter(id, password);
    }

이렇게 선언해서 나중에 body타입으로 받아주기

3. @RequestParam 사용하기 : RequestParam은 1개의 parameter를 받기 위해 사용되고있다. null 들어가면 안된다.

    @DeleteMapping("/post/{id}") //REQUESTPARAM 방식으로 구현, body : form-data
    public String deleteLetter(@PathVariable Long id, @RequestParam String password, Model m) throws JSONException {
        m.addAttribute("password",password);
        return letterService.deleteLetter(id, password);
    }

 

느낀점

정말 맛있는 과제였다. 덕분에 정말 HTTP request, response, 그리고 MVC패턴과 각종 어노테이션에 대해서 학습할수 있는 좋은 기회였다....

 

앞으로 남은 과제는 유효성 처리를 어느 Layer에서 할까에 대해 고민해보아야겠다.

 

 

 

 

https://velog.io/@jsb100800/Spring-boot-DTO-Entity-간-변환-어느-Layer에서-하는게-좋을까

 

[Spring boot] DTO <-> Entity 간 변환, 어느 Layer에서 하는게 좋을까?

Spring Boot 개발 중 학습이 필요한 내용을 정리하고,트러블 슈팅 과정을 기록하는 포스팅입니다.

velog.io

https://thalals.tistory.com/230

 

[Spring] JPA FindAll<Entity> to convert DTO (ModelMapper)

Spring에서는 DTO를 이용해서 사용자에게 Request를 받고, Response를 보냄으로써, 데이터를 보호한다. Request를 DTO로 받는방법은 정말 많고, 자료도 많지만 Response를 DTO로 변환하는 방법은 잘 나오지 않

thalals.tistory.com

https://frogand.tistory.com/163

 

[Spring] @RequestBody vs @ModelAttribute

이전 글 https://frogand.tistory.com/114 [Spring] @RequestBody, @RequestParam, @ModelAttribute의 차이 Client에서 받은 요청을 객체로 바인딩하기 위해 사용하는 방법에는 총 @RequestBody, @RequestParam, @ModelAttribute 총 3가지

frogand.tistory.com

https://mangkyu.tistory.com/72

 

[Spring] @RequestBody, @ModelAttribute, @RequestParam의 차이

이번에는 Spring에서 Client로 받은 요청을 객체로 바인딩하기 위해 사용하는 방법들에 대해서 알아보도록 하겠습니다. 1. RequestBody, ModelAttribute, RequestParam이란? [ @RequestParam ] @RequestParam은 1개의 HTTP

mangkyu.tistory.com

 

개괄

3주차를 시작하기에 앞서서 다음과 같은 과제의 제출을 요구받았다.

첫 번째는 BUS 클래스를 모델링하고, 두번째는 Requirements를 만족하는 BUS 클래스를 리팩터링 하는 것이다.

문제 발생

문제를 해결하는것은 그렇게 어렵지 않지만, 요구사항을 맞추면서 미래의 요구사항을 생각해서 만드는 '설계'가 쉽지 않았다. BUS 클래스는 어떻게 만들어야 할까? 그리고 BUS객체를 생성해주는 팩토리 메서드는 어떻게 생성해야 할까?

https://github.com/choincnp/Hanghae_4thweek

 

GitHub - choincnp/Hanghae_4thweek

Contribute to choincnp/Hanghae_4thweek development by creating an account on GitHub.

github.com

 

시도해본 것들

팩토리 메서드 패턴이란?

"구체적으로 어떤 인스턴스를 만들지는 서브 클래스가 정한다."

다양한 구현체(Product)가 있고, 그 중에서 특정한 구현체를 만들 수 있는 다양한 팩토리(Creator)를 제공할 수 있다.

OCP와 비슷한 맥락이다. 최대한 메서드를 팩토리 인터페이스에 빼놓고 구체적인 것만 하위 모듈에서 정한다.

핵심 : 새로운 클래스를 만들 때 기존 코드를 건드리냐 아니냐가 핵심

복습 :

팩토리 메소드 패턴을 적용했을때의 장점, 단점

  • 장점
    • OCP를 잘 지킬 수 있다. 객체가 추가되어도 if-else등의 구문에서 자유롭다.
    • 코드를 간결하게 구현할 수 있다.
  • 단점
    • 클래스가 너무 많이 늘어난다.

여기서 OCP란 무엇일까?

  • SRP와 마찬가지로 기존 클래스에서 어떤 클래스를 수정해야 할 때 기존 클래스를 건드리지 않고 새로운 인스턴스를 다른 방법으로 확장할 수 있어야 한다.
  • 새로운 클래스를 설계할때는 확장성이 있어야 한다.
  • 나중에 설명할 때 “무엇”이 확장이고 “무엇”이 변경인지 말해줘야 함.

DIAGRAM

설계한 것을의 Diagram을 뽑아내면 다음과 같다.

원래는 자바 11 위에서는 interface 안에 default modifier를 사용해 구현된 메서드를 넣을 수 있지만,

내가 아직 배우기로는 "인터페이스 안 모든 메서드는 public abstract여야 한다"이기 때문에, 팩토리 인터페이스 안에 모든것을 넣기보다는, 하위의 busfactory라는 추상 클래스에 의존하도록 설정했다.

나중에 혹시나 대형버스 / 소형버스별로 다르게 생성할 수 있어야 해서

그 busfactory를 상속받는 LargeBusFactory와 SmallBusFactory로 나눠 구현했다.

Bus 인스턴스와 Taxi 인스턴스는 Vehicle을 상속받으며, 위의 요구사항을 맞춰 설계했다.

버스의 상태는 Enum으로 운행중과 차고지행으로 나누었다.

 

가장 고민한 부분은, 요구사항 중 버스 객체는 고유값으로 설정되어야 하는데, 큰 버스와 작은 버스의 고유값은 겹치지 않게끔 해야하는 것이었다.

 

그래서

BusFactory 안에 busNum을 설정해두고,

BusFactory를 상속받는 하위 XXXBusFactory에 sequence를 설정해 두었다.

사실 이렇게 쓰고보니 꽤 간단한 일이었는데, 당시에는 정말 고민을 많이 했다.

 

팩토리 메서드의 단점으로 많은 클래스를 꼽았는데, 디렉토리로 나눠두면 오히려 더 깔끔하고 좋을 것 같다.

개괄

스프링 컨테이너는 자바에서만으로는 지키기 어려운 OCP와 DIP를 지킬 수 있게끔 도와준다.

하지만 OCP, DIP가 무엇인지 SOLID에서 깨닫지 못하면 말짱 도루묵이 된다.

또, SOLID를 이해하고 적용하기 위해선 상속과 인터페이스에 대한 이해가 있어야 한다.

상속과 인터페이스란 무엇일까?

 

목표

상속과 인터페이스를 알고, 설계의 핵심 원리에 다가갈 수 있게끔 한걸음 나아가보자.

 

배운 것 - TIL SERIES(LINK)

붕어빵 장사와 함께하는 클래스, 메서드, 추상 클래스와 인터페이스

 

 

팩토리 메서드 디자인 패턴과 함께하는 버스 공장 만들어보기

https://choincnp.tistory.com/44

 

TIL - 팩토리 메서드 디자인 패턴과 함께하는 버스 공장 만들어보기

개괄 3주차를 시작하기에 앞서서 다음과 같은 과제의 제출을 요구받았다. 첫 번째는 BUS 클래스를 모델링하고, 두번째는 Requirements를 만족하는 BUS 클래스를 리팩터링 하는 것이다. 문제 발생 문제

choincnp.tistory.com

 

목표 달성 여부

저번주와는 다르게, 이번주는 조금이나마 객체지향이 무엇인지를 알 수 있었다.

물론 개념은 디스크 조각 모음처럼 내 머리에 저장되고 있지만 이번주는 그 조각이 조금씩 연결되는 것 같아 기뻤다.

많은 것을 공부하지는 못했지만, 그래도 이렇게만 하면 언젠가는 발전할 수 있을것이라는 기대를 가졌다.

 

느낀 점

늘 그랬듯, 설계를 한다는 것은 내 눈앞에 보이는 것이 아니라 숲을 미리 보고 작성하는 것이고,

더 나아가서 보이지 않는 미래를 보며 꿈꾸고 꾸미는 것이 설계이다.

그래서 좋은 설계는 추상적이어야 하고, 추상적인 것에 의지해야 한다.

'인간 군상'이라는 말이 있듯, 클라이언트는 언제 어떤 요구조건을 들이밀 지 모른다.

그것이 가능하든, 불가능하든 최대한 생각해서 프로그래밍을 해야 한다.

 

다음 주 목표

누구나 인강을 들으면 '아~이거구나'라고 이해할 수 있다. 이해를 하지 못해도, 이해를 해도 같은 반응이 나온다.

그런데 또 막상 이걸 적용하려고 하면 굉장히 힘들다.

그렇지만 세상은 쳐맞는 기회를 받기조차 어려운 것이 현실이다.

그러나 흙 속에도 한줄기 빛이 있으니, 프로젝트나 나만의 코드를 짜면서 적용시켜보면 정말 빠르고 쉽게(?) 이해가 된다.

이번주도 프로젝트나 과제에서 배운 내용을 많이 이해해보도록 해야겠다.

개괄

이제 겨우 포트 3000과 8080은 연결했다.

그런데 내가 만들어야 하는것은 FE에서 준 query를 가지고 youtube에 get 방식으로 요청을 보내서 받은 response를 파싱해서 다시 FE로 보내주어야 프론트에서 그것을 렌더링할 수 있고, 그 기능을 실제로 구현해야 한다!

youtube api를 쓰면 쉽게 파싱할 수 있지만, 화이트카드를 계속 발급받으면서 Auth를 하는데 번거로움이 있어 그냥 대가리 박고 파싱하기로 했다.

 

문제 발생

근데 이거 어케함?

8080에 요청이 들어올때 response를 쥐어주는 법, request로 파싱하는 법은 알았다.

근데 내가 클라이언트 입장에서 다른 서버에 요청을 보내고 받아오는 것은 한번도 해보지 않은 기술이라 벌벌 헤멨다.

어찌저찌 유튜브에서 받아온 html(유튜브는 SSR 방식을 이용하기 때문에, 쉽게 말하면 빈 템플릿을 보내고 그 안에 이런저런 요청받은 정보들을 가져와서 렌더링한다). 은 또 어떻게 파싱할꼬...

 

시도해본 것들

일단 처음으로 youtube에 get 메서드로 통신하기 위해서는 여러 방법이 있었다.

  • HttpUrlConnection 사용하기 -> 너무 옛날 기술이라 요즘엔 잘 사용하지 않는다고 함
  • RestTemplate -> Spring 3 이후에 나온 기술, 현재는 또 Depricated당했다.
  • WebClient -> Spring 5 이후에 나온 기술

그중에 RestTemplate를 사용해보기로 했다.

UriComponentsBuilder를 이용해 Uri에 쿼리를 담아 보냈는데,

텅 빈 ResponseBody만 온다..

다음 코드의 문제점은 뭘까?

8080포트에서는 200번대로 다 떴는데, status code를 로그로 찍어보니 300번대가 떴다.

왜 301이 떴지? 하면서 온갖 생각을 다 하다가 하나를 생각해냈다.

HTTPS

HTTPS 프록시 기술을 사용하기 때문에 리다이렉팅을 하는 과정에서 불러오지 못했던 것이었다.

이걸가지고 3~4시간을 헤메다니...

아무튼 성공했고, 다음과 같은 responseBody를 받았다.

전체는 이거의 한 6배정도 되는 것 같다.

이거를~ 어떻게~ 파싱할까요~?

이 긴 String 사이에 JSON이 숨어있다. 그 숨겨진 JSON을 꺼내서 우리가 원하는 타이틀로 바꿀것이다.

jsoup은 html parser라 html이 변동되면 사용할 수 없기 때문에

youtube api를 쓰지 않으려면 일일히 파싱을 해 주어야 한다. (CSR은 깡통 html만 주기 때문에 jsoup을 쓸 수 없다.)

파싱은 JSONParser를 이용해서 파싱하려고 했다.Json.simple 라이브러리를 이용해서 바꾸어 주려고 하니

이 안에 파싱을 할 때 JSONObject로 파싱되는지, JSONArray로 파싱되는지에 따라 계속 확인해주어야 하고, 계속 형변환을 해 주었다.

JSONParser를 쓰는 레퍼런스들은 많아서 찾아볼만했지만 보통은

[{곤충1 : 잠자리}, {곤충2 : 매미}] 등의 단순화된 스트링에서만 파싱을 했고,

{asdfasdfdsfa:{asdfsdafsdfa{asdfdsaffdsafds:gdfadsaffdsafsadfdsa:sdaffsadfsadfdas:···}···}···}등의 구조를 알아보기 힘든 긴 JSON을 파싱하는 레퍼런스들은 없었기에, 일일히 해 주어야 했는데,

이 문장을 1억줄 쓰려니 생각만 해도 아찔했다.

바로 ObjectMapper의 등장이다.

ObjectMapper에서 readTree로 데이터를 읽으면 JsonNode로 반환한다. 더욱이나 좋은것은, get 메서드 안에 index와 fieldname을 둘 다 혼용해서 쓸 수 있기에 형변환을 안해주어도 된다는 점이 매우매우 마음에 들었다.

 

알게 된 것

머리를 박으면서 코딩을 하다보니 알게된것들이 있었다.

1. BE api의 flow

MusicParsercontroller에서 MusicParserService를 const injection을 통해서 주입하고 GetMapping으로 /search에서 FE에서 날려준 request를 가지고 MusicparserService.method(request)한다는 것

2. RestTemplate의 흐름

resttemplate 구현 => getForObject로 json형식의 string값을 받고(getForEntity써서 entity로 결과 반환받아도된다.)
=> 그다음에 그걸 service단의 muisicparser로 가져가서 파싱하고 =>그다음에 프론트에 정보 줌

 

그럼 이제 파싱을 해서 프론트로 던져주는 작업을 해 보자.

개괄

주특기 세션으로 들어가는 길목의, 어쩌면 가장 중요하면서도 가장 핵심적인 주

자바를 배우는 주간이었다.

개인적으로 Spring에서는 자바의 모든 문법들을 자유자재로 구사할수 있을 정도는 되어야 한다고 생각하는데,

그래서 자바에 대해서, 그리고 스프링을 하는 이유인 '객체지향'을 위해 얼마만큼 다가가느냐가 핵심이 되겠다.

 

목표

객체지향 프로그래밍이란 무엇인지 깨닫고 JVM은 왜 존재하는지, 그리고 JVM의 동작 원리에 대해 이해하고 CS지식은 왜 쌓아야 하는지를 알아야 한다.

 

배운 것 - TIL SERIES(LINK)

oop의 핵심요약

https://choincnp.tistory.com/28

 

TIL - OOP 요약

OOP 핵심정리 캡슐화 상속 추상화 다형성 OOP의 장점 유지보수 Easy 재사용성 ⬆️ 중복제거 Easy OOP 작성 순서 클래스 생성 (설계도 작성) 인스턴스 생성 (제품 생성) 인스턴스 사용 (제품 사용) 클래

choincnp.tistory.com

 

JVM이란 무엇인가요

https://choincnp.tistory.com/22

 

TIL - THE JAVA(1), JVM

개괄 IT 인프라 구조라는 책을 읽다가 문득 웹 서버와 AP 서버에 대한 이야기가 나왔다. 웹 서버의 OS 안에는 '프로세스'라는, 독립된 메모리 공간을 가진 것이 있다. AP서버에도 '스레드'라는 프로

choincnp.tistory.com

 

목표 달성 여부

객체 지향적 언어란 말 그대로 '지향'일 뿐이다. 객체 지향적 언어에 정답은 없다고 생각한다.

객체 지향에 다가가고 있어도, '이것이 객체 지향이다'는 말은 섣불리 할 수가 없었다.

SOLID 원칙을 배우면서 객체를 어떻게 구성해야 하는지에 대해서도 조금은 알 수 있었지만,

내가 앞으로 짜는 코드에 대해서 어떻게 객체지향적으로 짜야하는지는 아직 너무 이르기도 하고 디자인 패턴들을 많이 배워야 할 것 같다. JVM도 마찬가지다. CS지식이 전무한 내게 JVM의 시스템 구조를 조금 본 것은

혀에 잠깐 한 방울 닿은 정도로만 다가왔다.

그래서 이번주는 솔직히 점수를 많이 줄 수 없다고 생각한다.

조금 더 과장을 보태면 0점을 주고 싶다.

 

느낀 점

아는 것을 다시 공부하기는 굉장히 힘들다. 자바의 정석을 몇번이나 봐 온 나로써는 솔직히 조금 지루했다.

문제는 지루하면 안 된다는 것에 있다. 볼때마다 새로운 지식이 나오는데도 불구하고 무엇에 우선순위를 두어야 하는지에 대해 계속 뺑뺑 돌았다. 알고리즘 공부하다가 자바보다가 새로운 지식 보다가 하니 결국 남은게 없었다.

아는 형에게 정말 좋은 제안이 왔다. 스프링 개발을 부로, swift를 주로 하는 곳인데, 과장님이 나를 좋게 보신다고 이력서를 한 번 내보라고 했었는데, 그것에 대해 정말 고민했다.

그리고 나서 며칠 뒤 밤에, 같이 개발자를 목표로 했던 동료에게도 전화가 왔다. 면접을 봤었는데 스프링은 왜 쓰는지, 또 다른 개념에 대해선 무엇을 알고 있는지를 물어봤다고 했다. 곰곰히 생각해 봤는데 내가 거기에 대해서 답변할 수 있는게 너무 적었다. 그래서 부끄러워서 아는 형에게 조금 더 공부해야 할 것 같다고 연락을 남겼다.

이 상태로 취업을 했다간 나보다 나를 소개시켜준 형에게 부끄러워서 회사를 다니지 못할것 같았다. 취업시켜준다는 말은 안했지만... 정말 많은 것을 보고 듣고 알고 가야겠다고 생각했다.

 

다음 주 목표

하나에 몰두하지 않고 여러개를 돌린 시도 자체는 좋으나 결과가 너무 참담했다. 조금 더 개념에 치중해서 공부하고, 스프링 주간에 가면 할 게 많으니 빠트리지 말고 모조리 챙기고 가도록 하자. 

 

개괄

항해에 들어간 후 첫 프로젝트를 진행하였다.  

 

목표

프로젝트는 flask와 html, css를 활용한 youtube player를 구현하기로 했고,

session을 이용한 로그인 화면 구현과 bs4를 이용한 유튜브 search result를 크롤링해서 플레이리스트에 추가, 삭제하기

등이 주요 과제가 되었다.

 

배운 것 - TIL SERIES (Link)

REST-API : 누구나 읽기 쉽게 설계하기

https://choincnp.tistory.com/21

 

TIL - REST API : 누구나 읽기 쉽게 설계하기

REST란? REpresentational State Transfer의 약자로, 자원(리소스)의 표현에 의한 상태 전달을 하는, HTTP를 잘 활용하기 위해 만든 ‘아키텍쳐’이다. 따라서 REST를 잘 지키지 않는다고 해서 ‘trash code’나

choincnp.tistory.com

협업 - 합리적인 소통하기

https://choincnp.tistory.com/18

 

웹 개발 프로젝트 목표 설정하기

첫 팀이 분배되고, 프론트 지향 2명과 백엔드 지향 2명이 같은 팀이 되었다. 문제점 프로젝트 주제에 대해서는 많은 아이디어가 나왔지만, 그 주제에 어떤 기능을 넣어야 할지, 모여서 무엇을 해

choincnp.tistory.com

git flow - trunk-based 기법을 활용한 팀원들의 소통

db 저장하기 - Mongo DB를 활용한 dictionary를 element로 가진 Array를 저장하기

0주차 Troubleshooting

 

 

목표 달성 여부

결과적으로 말하자면, 절반의 성공과 절반의 실패라고 볼 수 있다.

GIT REPOSITORY : https://github.com/choincnp/duoback_mini

<main page>

좌측 상단 : iframe을 이용한 현재 재생중인 유튜브 영상

좌측 하단 : mongoDB를 이용한 내가 선택한 playlist

우측 상단 : search tab을 활용한 유튜브 영상 검색

우측 하단 : search result

<login>

pymongo를 이용해 로그인 페이지와 회원가입 페이지를 만들고, ID와 PW는 몽고DB에서 관리, 로그인 정보는 session에서 관리하게 만들었다.

클라이언트단은 완벽하지만, 서버단의 구조는 매우 빈약한것을 볼 수 있다. 조금만 더 알았더라면, 조금만 더 공부했더라면 다른 팀원들이 구현하고자 했던 기능들을 더 구현할 수 있었는데, 내 능력의 부족으로 이루지 못해 너무 속상했고, 각오를 다졌다.

 

 

느낀 점

컴퓨터는 앞으로만 가지만, 사람은 양방향성을 띈다.

앞으로만 가는 컴퓨터의 활동을 제어하는것은 쉽지만, 때로는 전진하고, 때로는 후퇴하는 사람을 제어하는것은 훨씬 힘든 일이다. 첫 협업에서부터 삐끗했던 활동을 생각하며, 옛날에 떠올렸던 '이런 사람과도 협업을 못하는데, 내가 과연 smooth한 협업을 진행할 수 있을까?'라는 생각을 다시금 했다.

 

내 코드는 컴퓨터만 읽는 게 아니라, 사람도 읽는다.

제발한번만

컴퓨터가 읽기 좋은 코드로 개발하는것도 좋지만, 사람이 읽기 좋은 코드로 개발하는것도 꼭 필요한 프로세스다.

읽기 쉽게 코딩하지 않으면 남들이 보는 데 거부감이 든다.

 

제대로 알지 못하면, 안 하느니만 못하다.

인강을 다 들었어도 실제 구현을 하려고 하면 막막하기도 하고, 도대체 내가 무엇을 하고 있는지, 어디에 있는지조차 헷갈린다. 

 

 

다음 주 목표

- 위의 것들을 기반으로 한 원활한 소통과 협업이 이루어지는, '케미스트리'가 가득한 오케스트라 같은 팀 구성하기.

- RESTful한 API 설계로 모르는 사람이 보아도 한 눈에 무슨 api인지 알게 하기

- code-convention과 commit-convention으로 명확한 의미를 지닌 코드/소통 설계하기

 

 

웹 서비스를 런칭하기 위해선...

  • 언제나 요청에 응답할 수 있어야 한다. (컴퓨터가 항상 실행 중이어야 한다)
  • '모두가 접근할 수 있는' 공개 IP주소로 내 서비스에 접근할 수 있어야 한다.

서버는 그냥 '컴퓨터'이기 때문에 우리는 가상 컴퓨터인 Amazon EC2(인스턴스)를 이용해 구동하기로 한다.

 

주의사항

아마존은 자선사업가가 아니기 때문에  컴퓨터를 무제한 제공해주지는 않는다.

free tier로 가입을 하더라도 컴퓨터를 계속 켜놓으면 돈이 나간다.

이런 메세지를 무시하다간 피같은 나의 돈이 나간다.

예전에 서버 구동을 따라해보고 깜빡 잊었다가 돈이 나갔었던 기억에 이같은 경고 메세지를 남긴다.

가끔 선량한 대학생이 1학년때 팀프로젝트로 EC2를 돌렸다가 까먹고 대학교 4학년때 billing으로 몇백만원이 청구되었다는 썰도 심심찮게 들리니, 

꼭 인스턴스를 '중지' 해주도록 하자!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

참고로 인스턴스 종료를 누르면 컴퓨터를 반납하는거라서 여러분들의 소중한 작업물이 다 날라간다.

 

EC2 연결하기

Git bash에서 SSH(Secure Shell Protocol)이라는 방식으로 접속을 할 건데, SSH방식은 그냥 조금 더 보안에 안전한 프로토콜이라고 생각하면 된다. pem키를 받아서 써야 한다.

모자이크된 부분에 본인의 키 위치를 넣고, ubuntu@뒤에는 인스턴스의 공용 IP Address를 누르면 된다.

만약 인스턴스를 중지했다가 다시 켤 경우, 공용 IP주소가 변하기 때문에 바꿔줘야 한다.

다음은 flask 환경에서 설정하는 3단계이다.

#1. python3 -> python
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10

#2. pip3 -> pip
sudo apt-get update
sudo apt-get install -y python3-pip
sudo update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1

#3. port forwarding
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 5000

 

  1. 첫 번째 명령어는 python3 ~~~~ 라고 해야하는 명령어를 python ~~~~로 바꿔주는 명령어다.
    프로그래머는 귀찮아해야한다. 그래야 더 나은 방식을 생각할 수 있으니까.
  2. 두 번째 명령어는 flask 환경에서 구동할 때 쓰이는 패키지들을 관리해주는 패키지 툴인 pip3를 설치하고,
    이름을 pip로 바꿔주는 것이다.
  3. 이게 가장 중요한데 우리가 계속 진행해왔던 localhost:5000에서 이 포트번호 5000을 포트번호 80으로
    리다이렉팅 해준다는 것이다. 리다이렉팅과 포워딩의 차이는 추후에 다시 설명하겠다.

그리고 Filezila에서 다시 연동하고 아마존의 인스턴스에 실행할 app.py나 index 등을 넣어준 후,
ls(list : 데이터 목록 보여주기)

cd(change directory : 디렉토리 변경)

mkdir(make directory : 디렉토리 생성)

pip install packagename(파이썬의 packang

등의 명령어를 입력해 '알아서 잘' 하고 python app.py 명령어로 가동시키면 된다.

 

인바운드 규칙 추가

인바운드 규칙 추가는 우리가 사용할 인스턴스의 포트번호를 추가해준다.

이렇게 TCP/5000 포트와 HTTP/80포트를 열어주어야 비로소 우리가 만들었던 홈페이지에 접속 가능하다.

 

80포트의 비밀

우리가 접속하는 대부분의 인터넷 사이트(네이버, 다음 등)는 사실 #http://www.naver.com:80 인데, 80포트가 생략된 것이다. 80은 생략이 가능하기 때문에. 그래서 아까 우리가 포트포워딩으로 5000번을 80에 연결해놓은 것이다. 그러면 접속할때마다 사이트 뒤에 5000포트를 생략해도 된다.

이 포트포워딩은 리눅스에서 자동 지원된다.

 

nohup 설정하기

nohup은 우리가 인스턴스에서 구동한 app.py를, git bash를 종료해도 구동될 수 있도록 하는것이다. 방법은 매우 쉽다.

그냥 git bash에 nohup python app.py를 써주면 계속 돌아간다.

나는 왜 그런진 모르겠지만 습관적으로 nohub를 써주는 경향이 있어 조금 더 주의를 요해야 한다. 왜 그럴까?

키는 건 알겠는데...

끌때는 다음과 같은 코드를 넣어주면 된다.

ps -ef | grep 'python app.py' | awk '{print $2}' | xargs kill

 

OG(Open Graph) tag

미리보기가 없다면 여기서 뭘 하라는건지, 무슨 사이트인지 모를 수도 있다.

OG태그는 위의 사진과 마찬가지로 미리보기 image, title, description등을 삽입하는 것이다.

그냥 <head></head>안에 다음과 같은 3줄을 심어주면 된다.

<meta property="og:title" content="내 사이트의 제목" />
<meta property="og:description" content="보고 있는 페이지의 내용 요약" />
<meta property="og:image" content="이미지URL" />

이로써 길다면 길고 짧다면 짧았던 5주차(라고 쓰고 5시간짜리) 수업이 끝났다.

 

웹 개괄을 이보다 더 잘 나타내준 수업은 없었다. 정말 감사한 수업이었다.

+ Recent posts