참고

https://github.com/codingspecialist/react-redux-example

 

codingspecialist/react-redux-example

Contribute to codingspecialist/react-redux-example development by creating an account on GitHub.

github.com

 

현재 프로젝트

https://github.com/jaybon1/react/tree/master/bitc_react/react-redux/redux-app

 

jaybon1/react

Contribute to jaybon1/react development by creating an account on GitHub.

github.com

 

esrint

prettier

 

 

--

 

 

프리티어 세팅파일 생성

--

 

 

깃헙에서 붙여넣기

프리티어 세팅

--

 

 

아래 것들을 삭제

--

 

 

--

 

 

구조

--

 

 

store를 만든다
1. 리듀서를 호출할 액션명을 적는다
2. state를 안쓰는 컴포넌트가 있을 수 있기 때문에 해당 컴포넌트용 state를 만들어준다
3. 리듀서 - state와 action을 받아서 액션명으로 함수를 실행한다.
리듀서는 내부적으로 setState가 내장되어 있어서 값만 넘기면 된다

--

 

 

리덕스를 설치한다

--

 

 

1. store로 만들어둔 리듀서 / Provider / createStore 를 가져온다
2. store.js파일로 진짜 스토어를 만든다
3. App컴포넌트를 Provider로 감싸고 store를 넘겨준다

--

 

 

--

 

 

리덕스를 통해서 함수를 받을 컴포넌트

--

 

 

1. 리덕스에서 connect 함수를 가져온다
2. 넘길 액션을 만든다 / 액션을 실행하면 onAdd 가 실행되고, dispatch가 내부적으로 리덕스 안에 있는 INCREMENT 로직을 실행한다
3. connect의 첫번째에는 state, 두번째에는 action / 이것을 담은 AddNumber 컴포넌트를 생성한다

--

 

 

리덕스를 이용하려면 connect를 export하는 Container를 사용한다
(리덕스를 이용하지 않으려면 AddNumber를 사용)

App.js에서 props를 넘기지 않는 다는 것을 보여주기 위한 파일.

--

 

 

리덕스를 통해서 state를 받을 컴포넌트

--

 

 

1. 리덕스에서 connect 함수를 가져온다
2. 넘길 스테이트를 만든다
3. connect의 첫번째에는 state, 두번째에는 action / 이것을 담은 DisplayNumber 컴포넌트를 생성한다

--

 

 

리덕스를 이용하려면 connect를 export하는 Container를 사용한다
(리덕스를 이용하지 않으려면 DisplayNumber를 사용)

App.js에서 props를 넘기지 않는 다는 것을 보여주기 위한 파일.

--

 

 

테스트

--

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Web > ReactJS' 카테고리의 다른 글

react js / next js quill 적용  (0) 2021.02.17
next js 시작하기  (0) 2021.02.15
리액트 // redux 리덕스  (0) 2020.08.12
리액트 // useEffect 연습  (0) 2020.08.05
리액트 // 스테이트를 훅스로 바꾸기  (0) 2020.08.05

 

URL패턴분석

 

크롤링을 막는 방식
1. Iframe
2. 연속적 Request
3. 레퍼럴 - Referrer - 어느페이지에서 접근했나

---------------------

파이썬
- BeautifulSoup
- 셀레니움 (인증이 필요한 페이지 등도 다 크롤링됨)

-------------

네이버 뉴스 분석

정치 주소
https://news.naver.com/main/main.nhn?mode=LSD&mid=shm&sid1=100

정치 기사 주소
https://news.naver.com/main/read.nhn?mode=LSD&mid=shm&sid1=100&oid=001&aid=0011814212

경제 주소
https://news.naver.com/main/main.nhn?mode=LSD&mid=shm&sid1=101

경제 기사 주소
https://news.naver.com/main/read.nhn?mode=LSD&mid=shm&sid1=101&oid=374&aid=0000218299

 

분석

sid1 = 카테고리 (100~ 105) - 6번 for 문

oid = 신문사 

aid = 네이버에 올린 기사글(넘버링)

 

mode=LSD / mid=shm 등 분석하기 어려운 것은 지워보고 화면이 어떻게 나오는지 확인하여 분석

 

언론사와 oid 확인 코드

더보기
package crawnewsapp;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

// 1번 JSOUP를 빌드패스
// 2번 JSOUP로 URL 요청
// 3번 oid의 번호는 어디까지 있는지 + oid마다의 신문사명을 매칭

public class OidParse {
	
	public static void main(String[] args) {
		
		Map<String, String> oidMap = new HashMap<>();
		
		Document doc = null;
		
		String oid = "0";
		
		int failCount = 0;
		
		for (int i = 1; i < 1000; i++) {
			if (failCount > 10) {
				System.out.println("언론사가 더 이상 없는 것 같습니다.");
				break;
			}
			
			if((i+"").length() == 1) {
				
				oid = "00"+i;
				
			} else if((i+"").length() == 2) {
				
				oid = "0"+i;
				
			} else if((i+"").length() == 3) {
				
				oid = ""+i;
			}
			
			try {
				doc = Jsoup.connect("https://news.naver.com/main/read.nhn?mode=LSD&mid=shm&sid1=101&oid="+oid+"&aid=0000000001").get();
			} catch (IOException e) {
				System.out.println(e.getMessage());
				int count = 0;
				for (int j = 1; j < 10; j++) {
					try {
						doc = Jsoup.connect("https://news.naver.com/main/read.nhn?mode=LSD&mid=shm&sid1=101&oid="+oid+"&aid=000000000"+j).get();
					} catch (IOException e1) {
						count++;
					}
				}
				if(count == 9) {
					failCount++;
					continue;
				} 
			}
			
			failCount = 0;
			
			String press = doc.select(".press_logo").select("img").attr("title");
			
			if (press.length() < 2) {
				int count = 0;
				for (int j = 1; j < 5; j++) {
					try {
						doc = Jsoup.connect("https://news.naver.com/main/read.nhn?mode=LSD&mid=shm&sid1=101&oid="+oid+"&aid=000000000"+j).get();
						if (doc.select(".press_logo").select("img").attr("title").length() > 1) {
							press = doc.select(".press_logo").select("img").attr("title");
							break;
						}else {
							count++;
						}
					} catch (IOException e1) {
						System.out.println(e1.getMessage());
					}
				}
				if(count == 4) {
					System.out.println("익명의 언론사는 있으나 내용이 없습니다.");
					continue;
				} 
			}
			
			System.out.println(press +" : "+ oid);
			if (press.length() > 1) {
				oidMap.put(press, oid);				
			}

		}
		
		System.out.println(oidMap);
		System.out.println(oidMap.size());
		
	}
}

 

--------------

 

리얼타임데이터베이스 - 관계형

클라우드파이어스토어 - NoSql

 

 

 

 

 

 

 

 

 

 

 

컬렉션

자바의 컬렉션과 비슷하지만

데이터베이스의 개념으로 생각하면 테이블이라고 생각하면 된다

 

 

 

 

자동ID - 프라이머리키를 안만들어도 자동으로 만들어준다, 속도가 빠르니 사용

자동ID 클릭

 

 

 

 

컬렉션을 문서형태로 넣는다고 보면된다

문서는 각각 다를 수 있다 (필드가 다를 수 있음)
- 제약이 없다 
- 정합성 무결성 보장이 되지 않는다
- 순서 보장이 되지않는다

 

 

 

똑같이 만드는 경우

 

필드를 다른형태로 만드는 경우

 

테스트용으로 총 5개를 넣어보자

 

순서가 보장되지 않기 때문에 createDate 등 날짜로 정렬하거나, 번호를 직접 넣어줘야한다

문서의 ID값을 필드에 넣어줘야 좋다

 

 

포린키

하나의 유저를 팔로우 할 경우, 

 

 

 

 

 

id -  팔로우할 id

 

 

팔로우 할 때 6을 넣는 것이 아니라 컬렉션을 만들어서 팔로우 한 사람들의 문서를 한번 더 적는 것

입력할 때는 번거롭지만 SELECT 할 때는 성능이 좋다

 

검색할 때

/user/6

/user/7/follow

RestApi와 비슷하다

---------

 

컬렉션 삭제

 

 

 

  username: ssar,
  password: 123,
  email: ssar@nate.com,
  phone:0102222,
  address:부산


위에서 받은 데이터를 firebase 메일 가입으로 넣고
Authentication - email , password 

생성된 UID를 포함해서 firebase store에 입력한다

유저 {
  id:1,
  uid: ajsdfjalsdf,
  username: ssar,
  password: 123,
  email: ssar@nate.com,
  phone:0102222,
  address:부산,
  date: 10시
}

 

post{
  id : 1,
  userId : 1,
  title: 제목,
  content: 내용,
  ~~
}

 

 

-----------

 

 

 

 

처음오셨다면 아래링크를 먼저 확인하세요

https://ondolroom.tistory.com/741

 

안드로이드 // firebase로 구글로그인하기

firebase 프로그래머의 필수사항이 되고 있다 push 기술 - 원래는 pull 기술 push 기술은 부하 때문에 잘 만들기 어렵다(while을 클라이언트만큼 돌려야하기 때문에 부하가 크다) firebase는 부하가 적은 pu

ondolroom.tistory.com

 

아래 사이트를 먼저 확인

https://firebase.google.com/docs/auth/android/start?authuser=0

 

Android에서 Firebase 인증 시작하기

Firebase에 앱 연결 Android 프로젝트에 Firebase를 추가하지 않았다면 먼저 추가합니다. 프로젝트 수준 build.gradle 파일의 buildscript 및 allprojects 섹션에 Google의 Maven 저장소가 포함되어야 합니다. 앱에 Fir

firebase.google.com

 

--

 

 

AndroidManifest.xml

인터넷 권한 획득을 위해 아래코드 추가

<uses-permission android:name="android.permission.INTERNET"/>

--

 

 

프로젝트 단위 build.gradle

'com.google.gms:google-services:4.3.3' 코드를 추가(3.0.0도 가능)

    dependencies {
        classpath "com.android.tools.build:gradle:4.0.0"
        classpath 'com.google.gms:google-services:4.3.3' // 추가

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }

--

 

 

앱 단위 build.gradle

맨 바깥에 아래코드 추가

// firebase
apply plugin: 'com.google.gms.google-services'

dependencies 안에 아래코드 추가

    //firebase create account
    implementation 'com.google.firebase:firebase-auth:19.3.2'

--

 

 

activity_main.xml

아래와 같이 뷰를 짜준다

--

 

 

MainActivity

더보기
package com.jaybon.firebasecreateaccounttest;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Main_Activity";
    private MainActivity mContext = MainActivity.this;

    private FirebaseAuth mAuth;
    private TextView tv;
    private TextView tv1;
    private EditText email1;
    private EditText password1;
    private Button sign;
    private Button login;
    private Button logout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Initialize Firebase Auth
        mAuth = FirebaseAuth.getInstance();

        tv = findViewById(R.id.tv);
        tv1 = findViewById(R.id.tv1);

        email1 = findViewById(R.id.email);
        password1 = findViewById(R.id.password);

        sign = findViewById(R.id.sign);
        sign.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                createAccount(email1.getText().toString(), password1.getText().toString());
            }
        });



        login = findViewById(R.id.login);
        login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                signIn(email1.getText().toString(), password1.getText().toString());
            }
        });


        logout = findViewById(R.id.logout);
        logout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                logout();
            }
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        // Check if user is signed in (non-null) and update UI accordingly.
        FirebaseUser currentUser = mAuth.getCurrentUser();
    }

    private void createAccount(String email, String password) {
        Log.d(TAG, "createAccount:" + email);
        if (!validateForm()) {
            return;
        }

        mAuth.createUserWithEmailAndPassword(email, password).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                Log.d(TAG, "onComplete: 들어옴");
                if (task.isSuccessful()) {
                    // Sign in success, update UI with the signed-in user's information
                    Log.d(TAG, "createUserWithEmail:success");
                    FirebaseUser user = mAuth.getCurrentUser();
                    tv.setText(user.getEmail());
                    tv1.setText("회원가입됨");
                } else {
                    // If sign in fails, display a message to the user.
                    Log.w(TAG, "createUserWithEmail:failure", task.getException());
                    Toast.makeText(mContext, "Authentication failed.",
                            Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

    private void signIn(String email, String password) {
        Log.d(TAG, "signIn:" + email);
        if (!validateForm()) {
            return;
        }

        mAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                Log.d(TAG, "onComplete: 들어옴");
                if (task.isSuccessful()) {
                    // Sign in success, update UI with the signed-in user's information
                    Log.d(TAG, "signInWithEmail:success");
                    FirebaseUser user = mAuth.getCurrentUser();
                    tv.setText(user.getEmail());
                    tv1.setText("로그인됨");
                } else {
                    // If sign in fails, display a message to the user.
                    Log.w(TAG, "signInWithEmail:failure", task.getException());
                    Toast.makeText(mContext, "Authentication failed.",
                            Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

    private void logout() {
        mAuth.signOut();
        Log.d(TAG, "signOut: 사인아웃!!!!!!!!!!!!!"+mAuth.getCurrentUser());
    }

    private boolean validateForm() {
        boolean valid = true;

        String email = email1.getText().toString();
        if (TextUtils.isEmpty(email)) {
            email1.setError("Required.");
            valid = false;
        } else {
            email1.setError(null);
        }

        String password = password1.getText().toString();
        if (TextUtils.isEmpty(password)) {
            password1.setError("Required.");
            valid = false;
        } else {
            password1.setError(null);
        }

        return valid;
    }
}

--

 

입력한 이메일과 비번이 공백인지 확인

--

 

 

회원가입을 진행하는 함수

--

 

 

로그인을 진행하는 함수

--

 

 

로그아웃을 하는 로직

--

 

 

회원가입 테스트

 

 

firebase 프로그래머의 필수사항이 되고 있다

push 기술 -

원래는 pull 기술

push 기술은 부하 때문에 잘 만들기 어렵다(while을 클라이언트만큼 돌려야하기 때문에 부하가 크다)

firebase는 부하가 적은 push기술을 가지고 있다

구글이 서버가 되고 내 앱이 클라이언트

 

Paas - 플랫폼 애즈 어 서비스 (파이어베이스를 통해서 로그인)

트래픽이적으면 돈이 안들고 트래픽이 늘면 돈이 든다

--

프로젝트 생성

--

 

 

firebase로 가서 구글 로그인 하자

--

 

시작하기 클릭

--

 

프로젝트 만들기 클릭

--

 

 

--

 

 

애널리틱스 - 웹에 달아놓으면 사람들이 해당 웹에서 이동한 경로를 분석 할 수 있다

유튜브처럼 사용자가 이용한 내역을 분석 - 사용자 추적

지금 프로젝트에서는 달지 않음.

(추후 자바스크립트에 달아서 테스트 해보기)

--

 

 

--

 

 

ios 안드로이드 웹에 추가 가능

안드로이드 선택

--

 

 

나의 진짜 패키지명을 넣어야 한다

다른 패키지명을 넣었을 경우 google-services.json파일을 수정하여 패키지명을 변경하면 된다

--

 

 

디버그 서명인증서 만들기

안넣으면 안되는게 너무많아서 넣어주자

--

 

 

안드로이드 스튜디오로 가서 오른쪽 Gradle을 눌러 signingReport를 클릭하면

아래와 같이 해시값이 나온다

--

 

위의 디버그 서명인증서에 SHA1 값을 넣어준다

--

 

--

 

다운로드한다

--

 

오른쪽 위 Android를 눌러 Project로 바꾸고 app폴더에 파일을 넣어준다

--

 

프로젝트 단위 빌드 그래들로 이동

아래코드를 dependencies 하위에 넣어준다

        // firebase
        classpath 'com.google.gms:google-services:4.3.3' // 안되면 3.0.0 입력

--

 

앱 단위 빌드 그래들로 이동

 

맨 바깥에 아래 코드를 입력한다

// firebase
apply plugin: 'com.google.gms.google-services'

dependencies 하위에 아래코드를 넣어준다

    //firebase
    implementation 'com.firebaseui:firebase-ui-auth:6.2.0'

--

 

 

콘솔로이동

--

 

문서로 이동

--

 

맨아래 왼쪽 인증버튼클릭

--

 

 

가이드를 확인한다

--

 

 

안드로이드에 firebase추가 하는법 설명 (가이드의 영상 및 설명을 참고하자)

https://firebase.google.com/docs/android/setup?authuser=0

 

Android 프로젝트에 Firebase 추가

기본 요건 Android 스튜디오를 설치하거나 최신 버전으로 업데이트합니다. Android 앱이 다음을 충족하는지 확인합니다. API 수준 16(Jelly Bean) 이상 타겟팅 Gradle 4.1 이상 사용 앱을 실행할 기기 또는 ��

firebase.google.com

--

 

로그인가이드 선택

--

 

 

앱 단위 빌드 그래들로 이동하여 dependencies 하위에 아래 코드를 추가해준다

implementation 'com.firebaseui:firebase-ui-auth:6.2.0'

--

 

 

구글로그인 선택

--

 

테스트할 레이아웃에 SignInButton과 로그아웃 버튼을 추가한다

--

 

메인 액티비티에 아래코드를 참고하여 추가

package com.jaybon.cosapp;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import androidx.appcompat.app.AppCompatActivity;

import com.firebase.ui.auth.AuthUI;
import com.firebase.ui.auth.IdpResponse;
import com.google.android.gms.common.SignInButton;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;

import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Main_Activity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SignInButton btnGoogleLogin = findViewById(R.id.btn_google_login);
        Button btnGoogleLogout = findViewById(R.id.btn_google_logout);


        btnGoogleLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // Choose authentication providers
                List<AuthUI.IdpConfig> providers = Arrays.asList(
                        new AuthUI.IdpConfig.GoogleBuilder().build());

                // 새로운 화면을 띄우는 것 (통신 아님)
                // 띄운후 로그인 하면 통신 (알아서 처리됨)
                startActivityForResult( // 화면이 꺼지면 리턴값을 받는것
                        AuthUI.getInstance()
                                .createSignInIntentBuilder()
                                .setAvailableProviders(providers)
                                .build(),
                        3648);
            }
        });

        btnGoogleLogout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FirebaseAuth.getInstance().signOut();
                Log.d(TAG, "onClick: 로그아웃" + FirebaseAuth.getInstance().getCurrentUser());
            }
        });
    }

    // 구글 로그인 화면 꺼지면 실행
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == 3648) {

            // 응답받은 데이터를 받는다
            // 동시에 오브젝트객체를 메모리에 띄워준다
            // firebaseAuth 힙영역으로 만들어서 사용자의 모든 정보를 담아준다
            IdpResponse response = IdpResponse.fromResultIntent(data);

            if (resultCode == RESULT_OK) {

                // FirebaseAuth 힙영역으로 만들어서 사용자의 모든 정보를 담아준다
                // 세션
                // 스태틱으로 만들어져있음- 모든 액티비티에서 접근가능 (앱은 나혼자 쓰니까)
                // 쉐어드프리퍼런스 안만들려면 이런식으로 만들어야함
                // Successfully signed in 로그인성공
                FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();

                // 로그인되었으니 인텐트해서 다른 액티비티로 이동하면 된다.
                Log.d(TAG, "onActivityResult: 로그인완료 : " + user.getEmail());
                Log.d(TAG, "onActivityResult: 로그인완료 : " + FirebaseAuth.getInstance().getCurrentUser());
                Log.d(TAG, "onActivityResult: 로그인완료 : " + response);


            } else {

                Log.d(TAG, "onActivityResult: 실패 : " + response.getError());
            }
        }
    }
}

 

 

메니페스트 파일에 인터넷을 권한 획득을 위한 코드를 넣는다

--

 

실행하여 구글 로그인 해보자

 

--

 

 

--

 

 

로그인해보면 아래 Users에 정보가 확인된다

--

 

 

 

 

 

 

 

 

 

 

세팅완료

----

 

 

 

 

 

 

'App > Android' 카테고리의 다른 글

안드로이드 // firestore 연습  (0) 2020.08.13
안드로이드 // firebase - email 가입 및 로그인  (0) 2020.08.12
안드로이드 // 툴바 헬퍼 공부 참고  (0) 2020.08.06
안드로이드 // MVVM  (0) 2020.08.06
안드로이드 // AAC  (0) 2020.08.06

 

자바는 타 클래스의 함수를 사용하기 위해서는 객체(레퍼런스)를 넘겨야 한다

자바스크립트(리액트)에서는 함수를 넘길 수 있다 (1급객체)

그런데 객체를 여러 단계로 넘기면 복잡해진다

객체지향에서 제일 어려운 것이 오브젝트를 넘기는 것이다

그러한 점을 해결하기위해 스프링에서는 IOC를 사용한다

스프링은 IOC공간에서 클래스타입으로 객체를 찾아낸다 -> 객체 안의 함수 모두를 가져올 수 있다

자바스크립트에서는 store에서 String값으로 함수를 찾아낸다(Action명) -> 

 

다른 화면에서 건드릴 것들을 예상해서 액션명을 미리 만들어둔다
아래는 store 공간에 만든 액션명들이다

[이벤트]를 등록하는데 다른화면에서 건드릴 것들 

최상위화면(A)에서 store를 만들고 자식들에게 props로 넘긴다

store(스프링의 IOC역할)
액션명 : A.change - (change()함수의 레퍼런스 + 해당화면의 state를 가진다)
액션명 : A.insert - (insert()함수의 레퍼런스 + 해당화면의 state를 가진다)

(액션명과 state 값을 가진 것을 리듀스 라고 한다)

 

다른 클래스가 store를 구독하면 접근을 할 수 있게 된다 (액션명을 호출하면 해당 함수가 실행된다)

레퍼런스를 실행하면 state값이 바뀐다

 

// A.js 화면 이벤트가 3개 있다
// onChange, onInsert, onDelete

// A의 자식 중에 B -> C -> D
// D가 A의 onChange를 건들이고 싶을 경우 기본은 props로 단계를 타고 함수를 전해줘야한다
// store는 모든 함수 레퍼런스를 저장하고 찾을 때 액션명으로 찾게 한다

// 1번 - 액션 이름 하나 생성
const ACHANGE = "a/CHANGE"; // 액션이름이 충돌 나지 않게 하기 위해서 a/를 붙여준다
const AINSERT = "a/INSERT";
const ADELETE = "a/DELETE";

// 앞에 파일명을 붙이고 뒤에 함수명을 붙여서 중복을 막는다
const DDELETE = "D/DELETE";

// 2번 - 액션을 함수로 호출할 수 있게 해야 함
const aChange = () => ({
  type: ACHANGE,
});

// const aChange = () => ({ // 여러줄이라도 소괄호로 묶어주면 한줄로 인식한다
//     type: ACHANGE
// });

const aInsert = () => ({
  type: AINSERT,
});

const aDelete = () => ({
  type: ADELETE,
});

const initialState = {
  number: 0,
};

// 3번 - 리듀서 만들기( 리듀서가 알고 있어야 하는 값 state, action)
// 프로젝트에 store는 하나로 관리한다
function aREducer(state = initialState, action) {
  switch (action.type) {
    case aChange:
      return {
        number: state.number + 10,
      };
    case aInsert:
      return {
        number: state.number + 1,
      };
    case aDelete:
      return {
        number: state.number - 10,
      };
  }
}

// 함수들을 익스포트
export { aInsert };
export { aChange };
export { aDelete };

// 리듀서를 익스포트
export default aREducer;

 

STORE 예시

리듀서(state, 액션이름){  }

요청하고 싶은 곳(D)에서 state와 aDelete를 store에 날리면 리듀서가 해당 레퍼런스를 이용해서 함수를 실행한다

state를 같이 날리는 이유는 D의 화면을 변경 하고 싶기 때문이다

 

(액션명과 함수 레퍼런스를 연결은 나중에)

 

리듀서에 액션이 많아지면 구분하기 힘들기 때문에 리듀서를 여러개 만들어서 컴바인 해서 합친다

 

 

 

 

 

 

 

 

 

'Web > ReactJS' 카테고리의 다른 글

next js 시작하기  (0) 2021.02.15
react // 리덕스 실습  (0) 2020.08.14
리액트 // useEffect 연습  (0) 2020.08.05
리액트 // 스테이트를 훅스로 바꾸기  (0) 2020.08.05
리액트 자동완성 스니펫  (0) 2020.08.04

 

--------------

참고프로젝트 jpa 메서드도 참고

https://github.com/codingspecialist/Springboot-Security-OAuth2.0-V2

 

codingspecialist/Springboot-Security-OAuth2.0-V2

Contribute to codingspecialist/Springboot-Security-OAuth2.0-V2 development by creating an account on GitHub.

github.com

 

oauth 세팅 유튜브

youtu.be/WACDTFroYNI

 

현재 프로젝트

https://github.com/jaybon1/springwork/tree/master/securityOAuthEx01

 

jaybon1/springwork

Contribute to jaybon1/springwork development by creating an account on GitHub.

github.com

---------

 

프로젝트 생성 후 이클립스 내에서 의존성 추가하기

--

 

OAuth2 Client 선택 추가

---

 

구조

--

 

application.yml

네이밍전략 참고

physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

https://mycup.tistory.com/237

 

Springboot jpa & Hibernate Naming Strategy(네이밍 전략)

Springboot jpa & Hibernate Naming Strategy 전략 어느날 부터인가 컬럼명이 카멜케이스로 나오고 있다. 왜이러지? Springboot 특정 버전 이상부터 프로퍼티 설정이 변경되었다. 프로젝트에 대소문자 구별이 ��

mycup.tistory.com

 

--

 

 

WebMvcConfig.java

configureViewResolvers를 사용하면 뷰리졸버의 확장자를 원하는대로 설정 가능하다

이클립스는 .mustache를 기본 지원하지 않기 때문에 html로 바꿔서 이용하도록 하자

--

 

 

SecurityConfig.java

@EnableWebSecurity // 필터체인을 커스텀 한다는 어노테이션

@EnableGlobalMethodSecurity // 어노테이션 기반 보안을 적용 할 수 있다

public interface BankService {

@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);

출처: https://yellowh.tistory.com/138 [노랭이의 공부공간]

WebSecurityConfigurerAdapter // 어댑터를 이용하면 원하는 보안기능만 커스텀 할 수 있다

--

 

추후에 만들 서비스인데 SecurityConfig 내부에서 사용하기 위해 @Autowired를 하였다

--

 

@Bean을 통해서 IOC 등록을 할 수 있다.

@Controller는 요청(request)가 올 때 IOC되므로 컨트롤러 이외의 IOC되는 공간에 Bean을 등록해야한다
(@Configuration 이나 @Component 등)

--

 

1. csrf 공격을 막는 기능을 비활성화 한다

2. /user 이후 모든 경로로 접속할 때 인증이 필요하다

3. /admin 이후 모든 경로로 접속할 때 ROLE_ADMIN 역할을 가지고 있어야 한다
(/admin** 이면 adminasdf 등의 경로로 접속할 때 ) 

4. 나머지 경로는 모두 허용한다

5. 폼로그인시 /login으로 리다이렉트 하고 로그인 요청시 /loginProc 주소로 이동

6. OAuth로그인시 설정
userInfoEndpoint() - OAuth2 로그인 성공 후 사용자 정보를 가져올 때의 설정들을 담당
userService(principalOauth2UserService) 소셜 로그인 성공 시 후속조치를 시행할 UserService 인터페이스의 구현체를 등록

--

 

해당 경로는 위의 시큐리티를 무시하고 접속하도록 함

 

 

UserRepository.java

JpaRepository를 상속하면 @Repository를 붙이지 않아도 IOC된다

JPA는 네이밍 전략으로 함수를 짤 수 있다는 점 참고 - 인터넷 검색

--

 

 

User.java

원래 모델에는 lombok의 @Data 를 사용하면 안된다 (연습용이기 때문에 사용)

@Id - 프라이머리키로 만들어줌(JPA)

@GeneratedValue - id 자동 증가 전략을 선택(JPA)

@CreationTimestamp - 타임스탬프 시간을 자동으로 넣어준다(JPA)

--

 

 

IndexController.java

@AuthenticationPrincipal // UserDetailsService에서 Return한 객체 를 파라메터로 직접 받아 사용할 수 있다

--

 

SecurityConfig에서 Bean으로 등록한 메서드를 DI 한다

--

 

비밀번호를 암호화하여 DB에 저장

--

 

@Secured // 컨트롤러에서 역할 체크를 하기 위해서 사용

--

 

 

PrincipalDetails.java

UserDetails는 Authentication 객체에 저장할 수 있는 유일한 타입이다 - 임플리먼트

OAuth2User는 OAuth2를 동시에 이용하기위해 임플리먼트

--

 

내부에 변수를 만들어도 되지만 User 타입 자체를 넣어도 무방하다

--

 

user에서 패스워드와 유저네임을 리턴

--

 

권한이 여러개일 수 있기 때문에 콜렉션으로 리턴한다

이것도 마찬가지로 SimpleGrantedAuthority라는 정해진 타입으로 만들어야 한다

--

 

OAuth2User를 임플리먼트 면 두개의 메서드를 오버라이딩 해야한다

getAttributes()는 google등 리소스 서버로부터 받는 회원정보를 담고 있다

 

 

PrincipalDetailsService.java

PrincipalDetailsService를 기본 UserDetailsService로 덮어 씌운다

loadUserByUsername()은 AuthenticationManager가 호출하는 메서드

--

 

 

OAuth2UserInfo.java

OAuth2 정보를 담을 인터페이스를 만든다

--

 

 

GoogleUserInfo.java

OAuth2UserInfo 타입을 임플리먼트 받아서 구글 전용으로 만든다

--

 

 

PrincipalOauth2UserService.java

스프링의 기본 DefaultOAuth2UserService를 상속 받는 커스텀 클래스를 만든다

 

토큰값으로 자원을 받아와서 세션에 등록한다

 

1. OAuth2 기능을 어떤 사이트(제공자)에서 사용했는지 확인

2. 제공자와 제공자의 ID를 받는다
Optional 타입을 이용하면 받은 객체가 null 이라도 내부에 저장되기 때문에 Optional 객체자체는 null이 아니라는 점을 이용할 수 있다

3. userOptional 내부의 객체가 null이 아니라면 내부 객체를 가져오고
null 이라면 User타입을 만들어서 DB에 저장한다

 

 

구글 oauth 설정

console.developers.google.com/apis

 

Google Cloud Platform

하나의 계정으로 모든 Google 서비스를 Google Cloud Platform을 사용하려면 로그인하세요.

accounts.google.com

 

 

 

앱 : 내서버

사용자 데이터 : 구글이 들고 있는 사용자 정보

 

 

아무것도 체크하지말고 만들기 클릭

 

스코프 기억

 

입력없이 저장

 

동의화면 만들어짐

 

사용자 인증 정보 만들기 다시 선택

 

 

 

 

CORS정책 : 다른 도메인에서 오는 자바스크립트 요청을 막음(이 수업에선 안쓸 것)

 

이것을 사용할 것

http://localhost:8080/login/oauth2/code/google

표준 주소이기 때문에 google 부분만 바꾸면서 사용

 

 

----

+ Recent posts