jun4928 / wanted-pre-onboarding-challenge-be-task-jan.2023 Goto Github PK
View Code? Open in Web Editor NEWJAN.2023 wanted 프리온보딩 챌린지 BE 사전과제
JAN.2023 wanted 프리온보딩 챌린지 BE 사전과제
// 사이드프로젝트의 일환으로 작성한 코드입니다.
// -- 필요하지 않은 객체의 인스턴스화를 최소화 하기위하여 static method로 만들어 보았습니다.
// -- 해당 방법이 옳은 방법인지 더 나은 방법이 있는지 궁금합니다.
public class ConvertQuestionImgae {
public static Question JpgToBase64 (Question question){
String basicPath = "/home/kai/mocktest/qpaper";
byte[] fileContent = null;
try {
fileContent = FileUtils.readFileToByteArray(
new File(basicPath+ "/"
+ question.getYy() + "/"
+ question.getExamCd() + "/"
+ question.getSubCd() + "/"
+ question.getQuestionPath()
)
);
}catch (Exception e) {e.printStackTrace();}
String encodedString = Base64.getEncoder().encodeToString(fileContent);
question.setImgBase(encodedString);
return question;
}
}
Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
-> 표현계층, 비지니스계층, 영속계층 등의 여러 개층으로 나누어진 아키텍쳐로
일반적인 웹에서 각계층의 역할은
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
-> 의존성 주입은 외부에서 자동적으로 인스턴스화를 진행하는 것입니다.
-> 필요한 이유는 서비스의 확장에 따른 부작용을 최소화 하기 위해서 입니다.
본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
-> 자바 StreamAPI
@Override
public List<List<Question>> getQuestion(Map<String, Object> subjectParams) {
return Arrays.asList(((String) subjectParams.get("subCds")).split(",")).stream()
.map(subCd -> new Subject(subjectParams,subCd))
.map(subject -> dao.getQuestion(subject))
.collect(Collectors.toList());
}
(코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
const execute = (() => {
try {
return f();
} catch (error) {
return "Error: failed";
}
})();
return new Promise((resolve) => setTimeout(resolve, seconds * 1000, execute));
}
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
언어 상관없음
어떤 로직이든 상관없음
단, 길이가 길지 않은 함수 단위가 좋습니다
def name_to_json(cursor):
row = [dict((cursor.description[i][0], value) for i, value in enumerate(row)) for row in cursor.fetchall()]
return row
위 코드는 DataBase에 연결하여 cursor.fetchall() 함수로 받아온 쿼리 결과를 json 형식으로 만들어 반환해주는 함수입니다.
레이어드 아키텍쳐 패턴에서의 구성 요소(component)들은 각 레이어에 수평적으로 구조화되어있다. 레이어드 아키텍처 패턴은 정확히 한 패턴에 몇개의 어떤 레이어가 있어야 하는지를 명시하지 않지만, 가장 보편적인 레이어드 아키텍처 패턴은 4개의 레이어로 구성되어있다; presentation, business, persistence, database이다.
- Dependency Injection(의존성 주입)의 개념
- 외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴으로, 인터페이스를 사이에 둬서 클래스 레벨에서는 의존관계가 고정되지 않도록 하고 런타임 시에 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있게 해준다.
- Dependency Injection(의존성 주입)이 필요한 이유
- 복잡한 프로젝트일 경우 지속적이고 효율적인 유지보수를 위해서 의존성 주입이 필요하다고 생각이듭니다.
순수 함수들을 조합하여 전체 프로그램을 구현하는 방식으로 말 그대로 프로그램 내에서 데이터 연산 및 처리를 수학적인 개념에서 이해하여 다루려는 개념
일급 함수는 아래와 같은 조건을 만족하는 함수로 대표적으로 kotlin, java script 함수들이 일급 함수에 속합니다. 파이썬 역시 모두 객체로 이루어져 있기 때문에 함수 역시 객체이고 따라서 파이썬의 함수 또한 일급 함수입니다.
- 변수나 데이터 구조 내부에 할당 가능
- 파라미터로 전달 가능
- 반환 값(return value)으로 사용 가능
- 할당에 사용된 이름과 무관히 고유하여 구별이 가능하고 동적 프로퍼티 할당이 가능
def factorial(n):
'''factorial func -> n : int'''
if n == 1 :
return 1
# 반환값으로 사용
return n * factorial(n-1)
# 변수로 할당
fac = factorial
>>> <function factorial at 0x10d30eb00>
# 인수로 전달
[fac(i) for i in range(1,6) if i %2 ]
>>> [1, 6, 120]
람다 계산법에서 만들어진 개념으로 일급 함수의 부분집합으로 볼 수 있습니다.
- 함수에 함수를 인수로 전달 가능
- 함수의 반환 값으로 함수를 사용 가능
함수형 프로그래밍에 꼭 필요한 개념입니다.
- 동일한 입력에는 항상 동일한 결과를 반환
- 함수의 실행이 프로그램 전체 실행과는 무관하다.(즉 side effect가 없다)
a = 10
def test(b):
a = 20
print(a)
print(b)
test(10)
>> 20
>> 10
print(a)
>> 10
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
after 2 seconds
successfully done
Error: failed
아예 모르는 내용에 대해서 새롭게 배워보고자 강의를 신청하였습니다. 해당 강의를 듣고 역량을 넓히고 싶습니다 화이팅!
1. 본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 code block 을 사용해 올려주세요
다음은 Google에서 검색된 apple앱의 ID를 받아오는 서비스입니다.
이전 프로젝트에서 구현했던 'Google Search Engine Api 서비스 입니다.' 구글에서 검색어를 입력하면, 검색된 링크에서 앱ID를 파싱했었어야 했습니다. 앱id의 형식은 "id389562983" 형태인데, 숫자는 9자리 또는 10자리가 될 수 있었습니다. 다음 코드는, 숫자만 추출하여 배열에 담아 return하는 함수입니다.
async findAppId(search: string) {
let filtered = [];
function isNum(val) { // 숫자인지 확인해주는 함수.
return !isNaN(val);
}
try {
const found = [];
const result = await this.httpservice.axiosRef.get(
`https://www.googleapis.com/customsearch/v1/siterestrict?key=${process.env.GOOGLE_API_KEY}&cx=${process.env.GOOGLE_ENGINE_ID}&q=${search}`,
);
for (const item of result.data.items) {
const words = await item.link.split('/id'); // 링크에서 '/id'를 기준으로 문자열을 나눈 후,
let appId = '';
for (const word of words) {
// 앱 id는 10자리, 9자리 두종류가 있음. 10자리일 경우 10자리 반환, 9자리일경우 9자리 반환
if (isNum(word.substr(0, 10))) {
appId = word.substr(0, 10);
break;
} else if (isNum(word.substr(0, 9))) {
appId = word.substr(0, 9);
break;
} else {
appId = '';
}
}
if (isNum(appId) && appId.length >= 9) found.push(appId);
}
filtered = [...new Set(found)]; // 중복제거
} catch (e) {
if (e) throw new NotFoundException();
}
return filtered;
}
2. Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
'계층형 아키텍쳐'는 역할에 따라 계층을 분리해 놓은 구조입니다. Nest.js의 경우 Controller, Module, Service, DTO 등으로 나뉘어 있고, 이렇게 역할에 따라 분리해 놓은 구조는 추후 유지보수에 용이함과 동시에 매우 깔끔하게 정리되었다고 주관적으로 생각합니다.
3. Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
(Nest.js프로젝트에서의 경험을 토대로 답변하겠습니다)
예를 들어, [Auth모듈, User모듈] 두개의 모듈이 있다고 가정하겠습니다. 각각의 모듈은 Controller, Module, Service, DTO를 가지고 있습니다. 하지만, Auth.controller.ts 에서 User.service.ts의 서비스를 끌어다 쓰고 싶으면, Auth.module.ts에 의존성을 주입해주어야 합니다.
쉽게 말해, 각각의 모듈은 각각의 기능을 가지고 있기에 분리되어 있는 구조입니다. 따라서, 다른 모듈의 기능을 가져다 쓰고 싶을 때, 모듈에 다른 모듈의 의존성을 주입해주어야 합니다. 이 의존성 주입은, 재사용성, 가독성을 증진시켜줍니다.
4. 본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
const array = [13, 14, 37, 42, 73, 77, 153, 777];
const array2 = array.filter((v) => (v % 7 === 0));
console.log(array2)
// 결과
// [14, 42, 77]
JS내에서 지원하는 메서드들이 있습니다. map, filter, reduce... 등. 위는 filter를 적용한 예시입니다.
5. (코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
try {
resolve(f())
} catch(e){
reject(e);
}
} , seconds * 1000);
});
}
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
6. 강의를 통해서 기대하는 바, 또는 얻고 싶은 팁을 적어주세요
Nest.js기반 백엔드 프로젝트를 경험하면서, 공식문서를 여러번 읽었습니다. 하지만 다양한 사람들과 함께 얘기 나누고, 배우면서, 더 깊이있는 배움을 가지고 싶습니다.
public RegisterGameResultDto joinGame(Long gameId,
Long currentUserId) {
User currentUser = userRepository.findById(currentUserId)
.orElseThrow(() -> new UserNotFound(currentUserId));
Game game = gameRepository.findById(gameId)
.orElseThrow(() -> new GameNotFound(gameId));
List<Register> registers = registerRepository.findByGameId(gameId);
Register register = game.join(currentUser, registers);
if (register != null) {
Register savedRegister = registerRepository.save(register);
Post post = postRepository.findById(game.postId())
.orElseThrow(PostNotFound::new);
User postAuthor = userRepository.findById(post.userId())
.orElseThrow(() -> new UserNotFound(post.userId()));
Notice notice = savedRegister.createRegisterNotice(
currentUser,
postAuthor
);
noticeRepository.save(notice);
}
return new RegisterGameResultDto(gameId);
}
위 소스코드는 개인 프로젝트에서 Spring Boot로 구현한 서버 애플리케이션의 Application Layer에 있는 Service 로직의 하나를 가져온 것입니다.
코드 중 Register register = game.join(currentUser, registers)
은 본래 다음의 구조였습니다.
registers.forEach(register -> {
if (register.userId().equals(accessedUserId)
&& (register.status().value().equals(RegisterStatus.PROCESSING)
|| register.status().value().equals(RegisterStatus.ACCEPTED))) {
throw new RegisterGameFailed("이미 신청 중이거나 신청이 완료된 운동입니다.");
}
});
List<Register> members = registers.stream()
.filter(register -> register.status().value().equals(RegisterStatus.ACCEPTED))
.toList();
if (members.size() >= game.targetMemberCount().value()) {
throw new RegisterGameFailed("참가 정원이 모두 차 참가를 신청할 수 없습니다.", game.id());
}
Register register = new Register(
accessedUserId,
gameId,
new RegisterStatus(RegisterStatus.PROCESSING)
);
리팩터링 이전의 구조는 Application Layer에 비즈니스 로직이 드러나 있는 형태였고, 테스트 코드로 코드의 동작을 검증하면서 구현을 진행하는 과정에서 테스트를 위한 데이터를 세팅하는 로직이 너무 복잡해져 테스트 코드를 작성하기 어려웠습니다.
다른 분들의 도움을 받아 비즈니스 로직을 객체가 수행하도록 리팩터링을 진행하면서 코드가 간결해지고, 테스트 코드의 크기가 줄어드는 것을 확인할 수 있었습니다. 이전까지는 단순히 Layered Architecture가 유연한 소스코드를 작성하기 쉽다고만 들었고 체감이 잘 되지 않았지만, 좋지 못했던 구조의 코드를 리팩터링하면서 Layer의 역할에 따른 관심사의 분리의 필요성을 체감하는 계기가 되었기에 해당 소스코드를 공유하게 되었습니다.
Layered Architecture는 소프트웨어 아키텍처의 한 종류로, 소프트웨어를 구성하는 conceptual한 요소들을 논리적으로 구조화하는 매커니즘인 Layer로 소프트웨어의 주요 동작인 Presentation, Application Processing, Data management를 논리적으로 분리합니다. Layer는 System infrastructure를 구성하는 하드웨어 요소들을 물리적으로 구조화하는 매커니즘인 Tier와 비교될 수 있습니다.
일반적인 객체지향 설계 환경에서 Layer는 다음의 4가지 계층으로 구분됩니다.
Layered Architecture의 가치는 관심사를 분리해 각 Layer에서 특정 영역을 집중적으로 다루게 하는 데 있습니다. 각각의 Layer에 관련된 코드를 Layer 별로 집중시킴으로써 로직이 분리되어 있지 않은 경우 대비 각각의 Layer를 훨씬 명료하고, 유연하고, 재사용 가능한 구조로 설계하고 구축할 수 있습니다.
Layer 간의 연결은 설계 의존성을 한 방향으로 두어 느슨하게 결합하도록 합니다. 일반적으로 상위 Layer이 하위 Layer의 인터페이스를 호출하는 식으로 이루어지고, 경우에 따라 하위 수준의 Layer가 상위 수준의 Layer와 통신해야 할 경우에는 콜백이나 관찰자 패턴을 적용하기도 합니다.
Dependency Injection은 디자인 패턴의 하나로, 객체 또는 함수가, 의존하고 있는 다른 객체 또는 함수를 전달받아 사용하는 패턴입니다.
Java의 웹 프레임워크인 Spring Boot에서 MVC 패턴을 적용하는 경우, 하위 Layer를 상위 Layer에 다음과 같은 방식으로 의존성을 주입할 수 있습니다.
// controllers/PlaceController.java
@RestController
@RequestMapping("places")
public class PlaceController {
private final GetPlaceService getPlaceService;
public PlaceController(GetPlaceService getPlaceService) {
this.getPlaceService = getPlaceService;
}
@GetMapping("{placeId}")
public PlaceDto place(
@PathVariable Long placeId
) {
return getPlaceService.getTargetPlace(placeId);
}
}
// services/GetPlaceService.java
@Service
@Transactional
public class GetPlaceService {
// ...
}
Dependency Injection은 다음과 같은 이점을 가집니다.
두 영역 간의 의존성 및 결합도가 감소합니다.
다른 객체나 함수를 전달받는 쪽에서는 전달받은 요소가 어떻게 구성되는지 구체적으로 알지 않아도, 인터페이스만을 아는 것만으로 요소를 사용할 수 있습니다. 소스코드의 관심사가 분리되므로 Layer나 요소의 재사용성, 유지보수성이 용이해집니다.
단위 테스트의 복잡성이 줄어듭니다.
특정 객체나 함수를 테스트할 때, 실제로 테스트하려는 대상이나 로직이 아닌 의존적인 객체나 함수를 stub이나 mock으로 구성하기 용이해집니다. 이로 인해 테스트 코드의 구조가 단순해지고, 실제로 테스트하려는 영역에 집중할 수 있습니다.
Java는 Java 8부터 함수형 프로그래밍을 위한 인터페이스로 람다식, Stream API, 함수형 인터페이스를 제공하고 있습니다.
Java의 람다식은 익명 함수를 구현하는 방식 중의 하나로, 다음과 같이 구성됩니다.
FunctionalInterface example = () -> System.out.println("message");
람다식을 구성하는 요소는 다음과 같습니다.
Stream API는 컬렉션이나 배열에 저장된 요소들에 순차적으로 혹은 병렬적으로 접근해 특정한 동작을 처리하는 메서드들을 지원하는 컬렉션입니다.
Stream API를 이용해 특정 컬렉션의 모든 요소를 화면에 출력하는 코드의 예시는 다음과 같습니다.
List<String> messages = List.of("Hello", "World", "How", "Are", "You");
messages.stream()
.sorted()
.forEach(System.out::println);
Are
Hello
How
World
You
Java에서 함수형 인터페이스는 1개의 추상 메서드를 갖는 interface를 의미합니다. 함수형 인터페이스는 람다식을 이용해 구현체를 생성할 수 있습니다.
Java에서 제공하는 함수형 인터페이스에는 Runner
, Supplier<T>
, Consumer<T>
, Function<T, R>
, Predicate<T>
등이 있습니다.
Runnable
Runnable runnable = () -> System.out.println("message");
runnable.run();
Supplier<T>
Supplier<String> supplier = () -> "message";
System.out.println(supplier.get());
Consumer<T>
Consumer<String> consumer = (message) -> System.out.println(message);
consumer.accept("message");
cf. 람다식이 하나의 인자를 받아 해당 인자를 특정 메서드의 인자로 넘겨주는 동작을 수행할 경우, method reference의 형태로 나타낼 수 있습니다.
// 위의 Consumer와 동일한 결과를 반환합니다.
Consumer<String> consumer = System.out::println;
consumer.accept("message");
Function<T, R>
Function<String, Integer> function = String::length;
System.out.println(function.apply("Hello"));
Predicate<T>
Predicate<String, Integer> predicate = (string) -> string.startsWith("a");
System.out.println(predicate.test("abcdefg"));
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
let message: string;
const checkError = () => {
try {
message = f();
} catch (error) {
if (error instanceof Error) {
const errorMessage = error.message
setTimeout(() => console.log(`Error: ${errorMessage}`), seconds * 1000);
}
}
}
checkError();
return new Promise<string>((resolve) => {
if (message) {
setTimeout(() => resolve(message), seconds * 1000);
}
});
};
백엔드 개발자로의 첫 취업을 준비하면서 Java와 Spring Boot를 학습하고, 개인 프로젝트를 진행하면서 학습한 것들을 적용해보는 것을 시도했습니다. 취업 준비를 위해 채용 공고를 둘러보면서 기술 스택으로 Java와 Spring Boot뿐만 아니라 JavaScript, TypeScript, node.js를 사용하고 있는 기업들도 많음을 확인할 수 있었습니다. Java뿐만 아니라 JavaScript, TypeScript를 이용해서도 백엔드 애플리케이션을 제작하는 역량을 기르고, 새로운 영역에 적극적으로 도전해 성취하는 경험을 하고 싶습니다.
@PostMapping(value = "/members/new")
public String create(MemberForm form) {
Member member = new Member();
member.setName(form.getName());
memberService.join(member);
return "redirect:/";
}
spring 을 공부하면서 PostMapping, GetMapping 을 사용하였던 코드의 일부 입니다.
이번 기회에 nest.js typescript를 이용하여 프로젝트를 만들고 싶습니다.
프로그래밍 개발에서 많이 채택 되는 아키텍처.
다른 계층에서 발생하는 문제나 알고리즘들은 굳이 신경 쓸 필요 X .
그러다 보니 유지보수와 테스팅이 쉬운게 장점.
총 4개의 계층으로 나누어서 진행
- Presentation Layer
- Business Layer
- Persistence Layer
- Database Layer
의존성 주입 개념
- 외부에서 두 객체 간 관계 결정해주는 디자인 패턴이다.
- 직접적으로 작성하는게 아닌 외부 의존관계에서 런타임 시 관계를 동적으로 주입해 주는걸 뜻한다.
의존성 왜 필요한가?
- 각 코드들이 강하게 결합되어 있을 경우 제약이 많아지고 확장성이 떨어진다.
- 의존성 주입을 하여 결합도를 낮추고 객체의 유연성을 높일 수 있기 때문
함수형 프로그래밍 정의
- 대입문을 사용하지 않는 프로그래밍, 문제 해결을 위한 작은 함수를 작성하는 뜻
let grade = [80,70,90,85,100]
grade.filter((value) => value>=90);
grade.sort(fun(a,b) {
return a-b;
});
grade.map((value) => value *2);
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
return new Promise<string>((sucess, fails) => {
setTimeout(() => {
try {
sucess(f())
} catch(e){
fails(e);
}
} , seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
이번 프리온 보딩 백엔드를 하면서 제가 가지고 있던 프론트 지식에 더불어 백으로 데이터 베이스를 연동하는 프로젝트를 진행하고 있습니다. 또한 그동안 기능성만 중시하였다면 가독성과 유지보수를 쉽게 하기 위한 노력의 발판으로 삼아 앞으로 나아가고 싶습니다.
const query = (params = {name : "이름", num : 1234}) => {
let setClauses = Object.entries(params).map(
([key, value]) => {
return typeof value === "string" ? `${key} = '${value}'` : `${key} = ${value}`;
});
return `SET ${setClauses.join(', ')}`;
};
// "SET name = '이름', num = 1234"
function solution(numbers) {
return numbers.reduce((a, b)=> a+b, 0)/numbers.length
}
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise<string>((resolve, reject) => {
setTimeout(()=>{
try{
resolve(f());
}catch(e){
reject(e);
}
}, seconds*1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
//controller
//인가코드와 state로 네이버 oAuth 토큰 가져오기(NestJS)
@Post('naver/token')
async getToken(@Body() { code, state }: NaverGetCode) {
const result = await this.authService.getNaverToken(code, state);
if (result === false) {
return '네이버 토큰발급 실패';
}
return `네이버 토큰 발급 성공`;
}
//service
//async await 사용
export class AuthService {
async getNaverToken(code: string, state: string) {
const data: any = {
grant_type: 'authorization_code',
client_id: process.env.NAVER_ID,
client_secret: process.env.NAVER_PW,
code,
state,
};
const response: any = await this.tokenFetch(data);
return response;
}
async tokenFetch(data: any) {
const queryString = Object.keys(data)
.map(
(k: any) => encodeURIComponent(k) + '=' + encodeURIComponent(data[k]),
)
.join('&');
const url = 'https://nid.naver.com/oauth2.0/token?' + queryString;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then((res) => res.json());
return response;
}
}
특정 역할로 구분되는, 예를 들어 화면 UI/비즈니스 로직/DB 작업등의 각각의 계층으로 설계하여 각 계층에 해당하는 역할만 수행하고 이 계층은 수직적으로 연결되어 있다. 유지보수와 확장 및 테스트가 쉽다는 장점이 있지만 단일 개체 서비스에서는 불필요한 연결과 의존하는 개체가 많아져 변경에 영향을 많이 받는 코드가 될 수 있다.
소스 코드의 수정, 변경 등에 영향을 많이 받는 강하게 결합된 클래스들을 분리하고, 애플리케이션 실행 시점에 객체 간의 관계를 결정해 줌으로써 결합도를 낮추고 유연성을 확보할 수 있다.
<?php
//$min 보다 큰 항목만 걸러내는 익명 필터 함수
function criteria_greater_than($min)
{
return function($item) use ($min) {
return $item > $min;
};
}
$input = [1, 2, 3, 4, 5, 6];
// 동적으로 만들어낸 필터 함수를 array_filter 에 전달해서 입력을 필터링한다.
$output = array_filter($input, criteria_greater_than(3));
print_r($output); // 3보다 큰 숫자만 출력된다.
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
return new Promise((resolve, reject) => {
const timeout = seconds * 1000;
const handler = () => {
try {
const result = f();
resolve(result);
} catch (error) {
reject(error);
}
};
setTimeout(handler, timeout);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
after 2 seconds
successfully done
Error: failed
NestJS 개념과 구조를 이해하고 새로 만들 애플리케이션에 적절한 방법을 찾아 잘~ 적용하고 싶습니다.
본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 code block 을 사용해 올려주세요
=> 공유하고 싶은 코드는 없습니다!
Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
계층 아키텍쳐는 소프트웨어 개발에서 가장 일반적으로 사용되는 아키텍쳐로, 관심사 분리를 통해서 각 계층마다 역할을 나누어 놓은 방식입니다.
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
의존성 주입이란, 클래스 간의 의존성을 외부에서 주입하는 것을 의미합니다. 클래스 간에 의존성이 있을 경우, 한 클래스에 변화가 생기면 다른 클래스가 영향을 받게 됩니다. 외부에서 의존성을 주입한다면, 의존성을 줄일 수 있으며 결합도는 낮추고 유연성을 높일 수 있습니다.
본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
자바스크립트에서 함수형 프로그래밍 함수에는 map, reduce, filter이 있습니다.
map은 해당 배열의 각 요소에 인자로 받는 함수를 실행시킵니다
let arr = [1, 2, 3]
arr = arr.map((el) => {
return el * 2 })
// [2, 4, 6]
위의 예제처럼 arr의 각 요소에 2를 곱하는 함수를 map의 인자로 전달하면, 각 요소에 2가 곱해진 배열을 리턴합니다.
type SomeFunctionReturnString = () => string;
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (e) {
reject(`Error: ${e.message}`);
}
}, seconds * 1000);
});
}
const success = () => {
return 'successfully done';
};
const fail = () => {
throw new Error('failed');
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
/**
* Nest.Js에서 계정을 생성할 때 게정 생성 일자와 암호화를 적용하는 로직
* 보통 이런 부분은 service 로직이나, 기타 영역에서 처리해야 하는데 Nest.Js에서는 스키마에 아래처럼 저장 전 핸들링을 할 수 있다는 부분이 마음에 들었습니다.
*/
AccountSchema.pre<AccountDocument>("save", function (next: Function) {
const saltRound: number = 10;
const accesDate: Date = new Date(new Date().getTime() + 9 * 60 * 60 * 1000);
this.createdAt = accesDate;
this.updatedAt = accesDate;
bcrypt.genSalt(saltRound, (err, salt) => {
if (err) return next(err);
bcrypt.hash(this.password, salt, (err, hash) => {
if (err) return next(err);
this.password = hash;
next();
});
});
});
//Typescript를 주로 사용하며, 순수함수, 비상태,불변성, 선언형, 1급 객체와 고차 함수 등을 사용합니다.
//Next.Js, Nest.Js 두 프레임워크를 사용하여 개발을 하며, 함수형 프로그래밍을 지향합니다.
let sevenCntPool: number[] = Object.keys(appearCnt).map((item: string) => Number(item)).filter((item: number) => appearCnt[item] === 1);
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
return new Promise((resolve, reject) =>
setTimeout(() => {
try {
resolve(f());
}
catch (e) {
reject(e)
}
}, seconds*1000),
);
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
`$ ts-node delay.ts
successfully done
Error: failed`
6. 강의를 통해서 기대하는 바, 또는 얻고 싶은 팁을 적어주세요
async getDailyChart(dayInfo: DayInfo): Promise<ChartData | null> {
//받은 날짜로 계산
const toDate: Date = new Date(dayInfo.date);
const fromDate: Date = new Date(toDate);
fromDate.setDate(toDate.getDate() - 6);
toDate.setDate(toDate.getDate() + 1);
const from: string = fromDate.toISOString().slice(0, 10);
const to: string = toDate.toISOString().slice(0, 10);
const updatedInfo: FromToInfo = {
user_id: dayInfo.user_id,
from,
to,
};
//전체 데이터 받음
const data = await this.calendarModel.findByDate(updatedInfo);
//인터페이스 초기화
const dailyData: ChartData = {
userId: dayInfo.user_id,
weight: [0, 0, 0, 0, 0, 0, 0],
kcalAvg: [0, 0, 0, 0, 0, 0, 0],
carbAvg: [0, 0, 0, 0, 0, 0, 0],
proteinAvg: [0, 0, 0, 0, 0, 0, 0],
fatAvg: [0, 0, 0, 0, 0, 0, 0],
kcalSum: 0,
carbSum: 0,
proteinSum: 0,
fatSum: 0,
sugarsSum: 0,
natriumSum: 0,
cholesterolSum: 0,
saturatedfattySum: 0,
transfatSum: 0,
};
if (!data || data.length === 0) {
return dailyData;
}
const chartSlotList: string[] = new Array(7);
for (let i = 0; i < 7; i++) {
chartSlotList[i] = String(
dayjs(fromDate).add(i, 'day').format('YYYY-MM-DD'),
);
}
let count = 0;
for (let day = 0; day < 7; day++) {
if (chartSlotList[day] === data[count].date.toISOString().slice(0, 10)) {
dailyData.weight[day] = data[count].todayWeight;
dailyData.kcalAvg[day] = data[count].currentKcal;
dailyData.carbAvg[day] = data[count].carbSum;
dailyData.proteinAvg[day] = data[count].proteinSum;
dailyData.fatAvg[day] = data[count].fatSum;
dailyData.kcalSum = data[count].currentKcal + dailyData.kcalSum;
dailyData.carbSum = data[count].carbSum + dailyData.carbSum;
dailyData.proteinSum = data[count].proteinSum + dailyData.proteinSum;
dailyData.fatSum = data[count].fatSum + dailyData.fatSum;
dailyData.sugarsSum = data[count].sugarsSum + dailyData.sugarsSum;
dailyData.natriumSum = data[count].natriumSum + dailyData.natriumSum;
dailyData.cholesterolSum =
data[count].cholesterolSum + dailyData.cholesterolSum;
dailyData.saturatedfattySum =
data[count].saturatedfattySum + dailyData.saturatedfattySum;
dailyData.transfatSum = data[count].transfatSum + dailyData.transfatSum;
count++;
}
if (count >= data.length) break;
}
return dailyData;
}
식단 관리 웹 어플리케이션을 제작할 때 차트 API를 위해 작성했던 서비스 함수입니다.
서비스의 기능은 아래와 같습니다.
Layered Architecture
계층 아키텍쳐는 자바 EE 어플리케이션의 사실상의 표준이 되는 등 널리 알려져있다.
계층화된 아키텍쳐 패턴 안의 구성 요소는 수평적으로 구성되며, 각각의 레이어는 어플리케이션 안에서 특별한 역할을 수행한다. 대부분 계층 아키텍쳐는 Presentation, Business, Persistence, Database, 네 개의 표준 계층으로 구성된다. 각 계층은 특정한 비즈니스 요구를 만족시키기 위한 작업을 추상화한다. 각 계층은 서로가 무슨 일을 하는지 알 필요가 없다.
계층은 표현 계층, 비즈니스 계층, 지속 계층, 데이터베이스 계층으로 나누어져 있다.
이처럼, 계층 아키텍처의 가장 강력한 기능 중 하나는 구성 요소간의 관심사 분리이다. 특정 계층 내에 있는 구성 요소들은 그 계층에 관련된 로직만을 다룬다.
또한 아키텍쳐 내의 계층은 전부 닫혀 있다. 이 뜻은 리퀘스트가 계층 간에서 이동할 때 바로 아래 계층을 통과해야 그 다음 계층 또한 통과할 수 있다는 것이다. 물론 바로 직접 리퀘스트를 보내는 것이 매우 빠를 테지만 격리 계층을 위해서 이와 같은 규칙을 지킨다. 예로, 만약 표현 계층에서 바로 비즈니스 지속성 계층으로 리퀘스트를 보낸다면 지속성 계층에서 행한 작업을 각각의 비즈니스 계층과 데이터베이스 계층에게도 반영을 해야 한다. 이렇게 된다면 아키텍처 계층간 상호 종속성이 높아지고 매우 밀접하게 결합된 어플리케이션이 되어 변경하기가 어려워진다.
리퀘스트 흐름은 보통 다음과 같다.
Dependency Injection 의존성 주입
의존성 : 의존대상이 변하면 의존하는 대상에게도 영향을 미치는 것
의존성이 높으면 변경을 자주 해야 하며 어려울지도 모른다. 하지만 의존 관계를 추상화하거나 하면 더 다양한 의존 관계를 맺을 수 있다.
이러한 의존 관계를 외부에서 결정하고 주입하는 것이 의존성 주입이다. 즉, 객체가 의존하는 다른 객체를 외부에서 선언한 뒤에 이를 주입 받아 사용하는 것이다.
장점
Scala를 공부 중입니다. 아직 공부 중이라 마땅한 예제가 없습니다.
스칼라는 객체 지향 언어의 특징과 함수형 언어의 특징을 함께 가지는 다중 패러다임 프로그래밍 언어이다. 스칼라는 모든 함수를 값으로 생각한다. 고차 함수를 지원하고 함수의 중첩을 허용하며 커링 또한 지원한다. 스칼라는 함수형 언어의 특징을 가지기 때문에 자바에 비해 코드 길이가 짧으며 바이트 코드를 최적화하여 자바보다 속도가 20% 정도 빠르다.
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
return new Promise<string>((success, fails)=> {
setTimeout(() => {
try{
success(f());
}catch(e){
fails(e);
}
}, seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
다른 분들과 꾸준히 챌린지를 도전하며 함수형 프로그래밍을 실무에 꼭 적용하고 싶습니다. 또한 트렌드 또한 익히고 싶습니다.
getChatRoomInfo(roomName: string): string[] {
const tmp: string[] = [];
this.rooms.get(roomName).users.forEach((e) => {
tmp.push(e.intra);
});
return tmp;
}
전체 채팅방을 임시 배열에 담아서 return해주는 코드
Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
각 계층은 어플리케이션 내에서의 특정 역할과 관심사(화면 표시, 비즈니스 로직 수행, DB작업 등) 별로 구분된다. 이는 Layered Architecture의 강력한 기능인 '관심사의 분리' 를 의미한다. 특정 계층의 구성요소는 해당 계층에 관견된 기능만 수행한다. 이런 특징은 높은 유지보수성과 쉬운 테스트
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
개념 : 객체가 의존하는 또 다른 객체를 외부에서 선언하고 이를 주입받아 사용하는 것!
장점 1 : 의존성이 줄어든다.
장점 2: 재사용성이 높은 코드가 된다
장점 3: 테스트하기 좋은 코드가 된다.
장점 4: 가독성이 높어진다!
본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
var condition = function(x) { return x % 2 === 0; }
var ex = function(array, cond) {
return array.filter(cond);
};
ex(arr, condition);
condition이라는 변수를 인자로 받아서 조건에 맞는 arr배열 내의 값을 return 하는 순수함수 입니다.
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (e) {
reject(e);
}
}, seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
async parseAllCategory(allCategory) {
let categoryIds = [];
let categories = [];
for (let i = 0; i < allCategory.length; ++i) {
categoryIds.push(allCategory[i]._id);
}
// 메인 카테고리 수 만큼 배열을 순회
for (let i = 0; i < categoryIds.length; ++i) {
let subCategoryNames = [];
const subCategories = await this.subCategoryModel.findByCategoryIdAll(categoryIds[i]);
// 메인 카테고리에 해당하는 서브카테고리 수 만큼 배열을 순회하며 서브카테고리명을 배열에 저장
for (let j = 0; j < subCategories.length; ++j) {
subCategoryNames.push(subCategories[j].subCategoryName);
}
let { categoryName } = await this.categoryModel.findById(categoryIds[i]);
// { 키: 메인카테고리명, 값: 서브 카테고리 배열} 객체 생성후 배열에 push
let tmpCategory = {};
tmpCategory[categoryName] = subCategoryNames;
categories.push(tmpCategory);
}
return categories;
}
client
에 전달하기 위해 작성한 파싱함수arr.reduce(callback[, initialValue])
var arr = [1,2,3,4,5,6,7,8,9,10];
arr.reduce(function(prev, cur) {
return prev + cur;
}); // 55
type SomeFunctionReturnString = () => string;
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
}catch (e) {
reject(e);
}
}, seconds * 1000);
});
}
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
$ ts-node delay.ts
successfully done
Error: failed
본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 코드블락을 사용해 올려주세요
import { Request, Response, NextFunction } from "express";
import BadRequestException from "../Common/Exceptions/BadRequest.Exception";
import ResultService from "./Result.Service";
class ResultController {
resultService: ResultService;
constructor(resultService: ResultService) {
this.resultService = resultService;
this.formResult = this.formResult.bind(this);
}
async formResult(req: Request, res: Response, next: NextFunction) {
const { formId } = req.params;
if (!formId || typeof formId !== "string") {
next(new BadRequestException());
return;
}
try {
await this.resultService.init(formId);
const result = this.resultService.formResult();
res.status(200).json(result);
} catch (error) {
next(error);
}
}
}
export default new ResultController(new ResultService());
Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
정의
계층별 역할
계층간 격리
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
// count라는 폐쇄된 유니크한 상태를 갖는 함수(counter)를 만들어내는 클로저 함수
function createCounter() {
let count = 0;
return function() {
count++;
return count;
}
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
const person = {
name: 'kim',
age: 27,
job: 'developer'
};
// person 객체에 불변성 부여
Object.freeze(person);
// 아래와 같이 직접 프로퍼티 변경시 에러가 발생한다.
person.age = 28;
// 새로 나이를 업데이트 하기 위해서는 새로운 객체를 생성해야 한다.
// 전개 연산자로 기존 객체를 깊은 복사한 후 수정할 값을 넣는다.
const updatedPerson = Object.freeze({
...person,
age: 28
});
console.log(person.age); // 27
console.log(updatedPerson.age); // 28
(코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
return new Promise<string>((resolve, reject) => {
const cb = (): void => {
try {
const result = f();
resolve(result);
} catch (error) {
reject(error);
}
};
setTimeout(cb, seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
강의를 통해서 기대하는 바, 또는 얻고 싶은 팁을 적어주세요
1. 본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 code block 을 사용해 올려주세요
export function myPromiseAll(arr) { if (!arr?.length) return Promise.reject('No promise!'); return new Promise((resolve, reject) => { const data = []; let pending = arr.length; console.time('Async!'); arr.map((el, idx) => el(idx + 1) .then((res) => { data[idx] = res; pending--; if (pending === 0) { resolve(data); console.timeEnd('Async!'); } }) .catch((err) => reject(err)) ); }); }
자바스크립트를 공부할 당시 자바스크립트 자체 내장 모듈들을 직접 구현해보며 공부했던 경험이 기억에 남습니다. Promise에 관한 온전한 이해가 쉽지 않았던 상황에서 해당 메소드 들을 구현해보며 Promise의 작동 방식에 대해 조금 더 이해할 수 있게 되었습니다.
2. Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
계층 아키텍처는 비지니스 로직과 UI로직을 분리하여 독립된 모듈로 나누어서 구성하는 API패턴입니다. 각각 역할에 따라 연결되어 전체의 시스템을 구현하는 아키텍쳐입니다. 일반적으로 Presentation layer, Business layer, Persistence layer로 구성되어 있으며 복잡하지 않고, 비용도 저렴하므로 소규모 프로젝트에 적합한 아키텍쳐라고 보여집니다.
3. Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
Dependency는 의존한다는 의미입니다.
예를 들어 A는 B에 의존하는 관계가 있다면, B가 변화할 시 A도 변화하기 마련입니다.
Dependency Injection이란 이러한 의존성을 클래스 내부에서 결정하는 것이 아니라 외부에서 사용자가 의존관계를 결정하고 주입하는 것을 의미합니다.
실제 코드에서는 의존관계를 인터페이스로 추상화 하여 외부에서 생성자를 통해 클래스 내부로 주입시키는 방식으로 진행됩니다.
이러한 DI가 필요한 이유는 인터페이스 추상화를 통해 진행되기 때문에 주입받는 대상이 변하더라도 코드 자체를 수정할 일이 줄어듭니다. 이를 통해서 의존성을 줄일 수 있습니다. 또한 클래스 분리가 쉬워지며 이로 인해 테스트하기가 용이해집니다.
4. 본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
함수를 인수로 받을 수 있고, 함수의 반환 값으로써 사용할 수 있는 함수들을 고차 함수라고 부르며, Javascript에서 이를 지원하고 있습니다. 이를 바탕으로 콜백함수로 활용이 가능합니다. 또한 렉시컬 환경을 통해 클로저를 활용할 수 있으며 이를 통해서 순수 함수를 구현할 수도 있습니다.
5. (코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string;
const SECONDS: number = 1000;
const SUCCESS_STATEMENT: string = 'successfully done';
const FAIL_STATEMENT: string = 'failed';
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (e) {
reject(e);
}
}, seconds * SECONDS);
});
}
const success = () => {
return SUCCESS_STATEMENT;
};
const fail = () => {
throw new Error(FAIL_STATEMENT);
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
프리온보딩을 진행하며 JS로 백엔드를 공부하는데에 도움이 될 만한 지식들을 얻어가고 싶습니다. 일급함수를 지원하는 JS의 이점을 살려 함수형 프로그래밍 또한 잘 사용할 수 있도록 학습하고 싶습니다. 추후 OOP와 FP를 잘 활용하여 유지보수하기 좋고 가독성이 좋은 코드를 작성하는 개발자로 성장하기 위한 조그마한 발판으로 삼고 싶습니다.
공유하고 싶은 코드는 없습니다.
코드의 유지보수나, 확장의 용이성을 위하여 관심사별로 계층을 분리하는 아키텍처입니다.
주로 Presentation Layer, Business Layer, Persistence Layer, Database Layer등으로 구성합니다.
책임을 나눠 코드를 작성하다 보면 코드 간의 의존관계가 생기게 됩니다.
DI는 의존관계를 직접 작성하는 것이 아닌 런타임 시점에 외부에서(스프링 컨테이너, 네스트 프로바이더 등등..)
결정하고 주입해주는 것입니다.
코드로서 의존관계를 모두 선언한다는 것은 각 코드들이 강하게 결합되어 있다는 뜻이고, 이는 유지보수의 큰 걸림돌이 됩니다.
또, 객체지향 설계 원칙 중 OCP와 DIP를 지키는 좋은 방법이 됩니다.
명령형으로 for, if등을 사용하는 것이 아닌 자료의 흐름에 따라 선언형으로 코드를 작성하는 것입니다.
JS에서는 함수가 1급 객체이기에 함수를 인자로 사용할 수 있습니다.
const pow = n => n*n;
const arr = [1, 2, 3, 4, 5];
console.log(arr.map((i)=> i*i));
console.log(arr.map((i)=> pow(i)));
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise<string>((resolve, reject)=>{
setTimeout(()=>{
try{
resolve(f());
}
catch(e){
reject(e);
}
}, seconds*1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
failed
흔히 함수형이라고 하면 JAVA에 Stream API와 JS에 map, filter, reduce 등등..
순회에 관련된 것 이상으로는 생각하기 어렵습니다.
이번 강의를 통해 함수형 패러다임을 더 깊게 이해하고 싶습니다.
``
public static String awards(Cars cars) {
for (Car car : cars.getCar()) {
if(car.getDistance() == maxDistance) {
winner += car.getName() + ",";
}
else if(car.getDistance() > maxDistance) {
maxDistance = car.getDistance();
winner = car.getName() + ",";
}
}
return winner.substring(0, winner.length() - 1);
}
public static String awards(Cars cars) {
int maxDistance = cars.getCar().stream()
.mapToInt(v -> v.getDistance())
.max()
.getAsInt();
cars.getCar().stream()
.filter(car -> car.getDistance() == maxDistance)
.collect(Collectors.toList())
.forEach(car -> {
winner += car.getName() + ",";
});
return winner.substring(0, winner.length() - 1);
}
리팩토링을 진행하면서 기존 소스를 stream API를 이용하여 람다식으로 구현을 해 보았습니다.
매번 예제를 보며 따라치다가 제가 직접 생각하고 작성한 코드라 인상 깊어 선정하게 되었습니다.
계층형 아키텍쳐는, component들이 각 레이어에 맞게 수평적으로 구조화 되어 있다.
구성되는 계층의 숫자에 따라 N 계층 아키텍처라고 부를 수도 있다.
레이어가 정확히 몇 개로 구성 되어 있다 정할 수 없지만, 크게 3계층과 4계층 구조를 이루고 있다.
3계층
4계층
또는 정확한 레이어 구성이 정해진게 아니기에 business 와 persistence 사이에 service 계층이 추가 되어 사용 될 수 있다.
장점으로는
의존성 주입이란. 외부에서 두 객체간의 관계를 결정해주는 디자인 패턴.
먼저 의존성이란 클래스간에 의존 관계가 있다는 것을 의미한다. 이 의존 관계라는 것은, 한 클래스가 바뀌게 되면 다른 클래스에 영향을 받는 것을 의미한다.
주입이란, 클래스 외부에서 객체를 생성하여 해당 객체를 클래스 내부에 주입하는 것을 의미한다.
그렇기에 의존성 주입을 하게 되면, 클래스간의 결합도가 약해지게 되면서, 한 쪽 클래스가 변경 될 경우 다른 한 쪽의 클래스 변경될 필요성이 적어진다는 것이다.
클래스간의 결합도까 작아져 유연성과 확장성을 향상 시킬 수 있으며, 인터페이스 기반 설계에 테스트 및 코드의 재사용성을 높여줘 리팩토링에 용이하다.
[참고]
https://kotlinworld.com/64
public BallStatus play(Ball input) {
return balls.stream()
.map(answer -> answer.play(input))
.filter(BallStatus::isNotNoting)
.findFirst()
.orElse(BallStatus.NOTING);
}
자바에서도 8버전부터 람다식이 도입되면서 대표적으로 Stream API을 통해 함수형 프로그램밍이 가능해졌습니다.
Stream의 특징으로는, 원본 값을 유지하되, 데이터를 활용 할 수 있다는 것 입니다.
또, 단점으로는 어디 부분에서 오류가 났는지 디버깅이 어렵다는 것 입니다.
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
}
catch (error) {
reject("" + error);
}
}, seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
console.log("after 2 seconds");
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
after 2 seconds
successfully done
Error: failed
아직 함수형 프로그래밍이 낯설어 쓰려고해도 주춤거리고 결국 기존 방법대로 사용하고 있기에, 이번 강의를 통해 자신있게 코드를 작성할 수 있도록 발전하고 싶습니다. 그리고 이런 강의 자체가 처음이라 강의 마지막이 되었을때, 배움에 있어서 내가 성장했구나라는 뿌듯함을 느껴보고 싶습니다
1. 본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 코드블락을 사용해 올려주세요.
언어 상관없음
어떤 로직이든 상관없음
단, 길이가 길지 않은 함수 단위가 좋습니다
테스트 코드 작성시 한글 메소드를 활용하면 이해하기 더 쉬운 테스트 코드를 작성 할 수 있어서 공유 드립니다.
@DisplayName("Bearer Auth")
@Test
void myInfoWithBearerAuth() {
//when
final ExtractableResponse<Response> 토근_로그인_응답 = 토큰_요청("[email protected]", "password");
//then
로그인_됨(토근_로그인_응답);
}
public static ExtractableResponse<Response> 토큰_요청(String email, String password) {
TokenRequest tokenRequest = new TokenRequest(email, password);
return RestAssured.given().log().all()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.body(tokenRequest)
.when().post("/login/token")
.then().log().all()
.extract();
}
private void 로그인_됨(ExtractableResponse<Response> response) {
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value());
assertThat(response.as(TokenResponse.class).getAccessToken()).isNotEmpty();
}
2. Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요.
계층 아키텍처 패턴의 각 계층에는 애플리케이션 내에서 특정 역할과 책임이 있어 특정 계층에는 특정 행위만 담당함
레이어드 아키텍쳐 패턴에서의 구성 요소(component)들은 각 레이어에 수평적으로 구조화되어있다. 레이어드 아키텍처 패턴은 정확히 한 패턴에 몇개의 어떤 레이어가 있어야 하는지를 명시하지 않지만, 가장 보편적인 레이어드 아키텍처 패턴은 4개의 레이어로 구성되어있다; presentation, business, persistence, database이다.
* Presentation layer: UI, 브라우저에서의 유저와의 통신 로직을 다룸.
* Business layer: 요청에 따른 비즈니스 로직을 다룸.
* Persistence layer: DAO(Data Access Object) , ORM과 관련된 데이터베이스에 접근해서 데이터를 저장, 수정하는 함수 로직을 다룸.
* Database layer: 데이터가 저장되어있는 곳. 데이터베이스.
개인적으로 레이어드 아키텍처는 의존성관리와 응집성을 높히는데 중점을 둔다고 생각이 되며 단일책임원칙과 잘어울리는 아키텍처라고 생각이되며
단점으로 레이어(계층)이 늘어날경우 복잡도가 증가할수도 있겠다라는 생각이 듭니다.
참조
1. 레이어드 아키텍쳐( Layered Architecture)
2. 오레일리
3. Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
DI 의존성주입이란 의존하는 객체가 직접 의존객체를 생성하지 않고 주입(Set나 ,생성자 ,필드 인젝션 등) 주입 받아서 생성되는 형태를 말한다.
의존하는 객체란? (예시)
public class AhasC {
C c;
AhasC(){
// A객체 생성안에서 C를 생성하고 있다 굉장히 강한 의존관계
c = new C();
}
}
DI 를 하게 되면 객체 외부에서 객체가 주입하게 되어 객체 생성시점이 아닌 외부에서 생성이 되기 때문에 온전히 객체를 본연의 객체를 생성할 수 있다.
가령 A라는 객체 안에서 B라는 객체를 생성한다고 가정을 해보자 B라는 객체의 생성 파라미터가 수정이 되었을때 A라는 객체도 수정되어야한다. 이는 단일책임원칙에 위반된다고 생각이 된다.
DI 사용한 예시
public class AhasC {
C c;
//C 의객체의 파라미터가 변경되어도 AhasC 객체에는 영향이 없다.
AhasC(C c){
this.c = c;
}
}
4. 본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
JAVA언어의 Functional Programming은 Stream 패키지로 구현할 수 있습니다.
Collections의 요소들을 이용하여 함수형 연산을 지원합니다.
대표적인 함수
List<Integer> number = Arrays.asList(1, 2, 3, 4, 5);
// 곱연산된값의 새로운 리스트를 반환
List<Integer> square = number.stream().map(x -> x * x)
.collect(Collectors.toList());
filter(): filter()는 스트림이 가진 요소를 정의한 함수를 이용해 선택하고 새로운 스트림을 반환합니다. 주로 특정 값을 찾아야 하는 경우에 사용합니다.
List<String> values = Arrays.asList("aa", "ab", "bb", "ba");
//a가 포함된 문자열을 필터링하여 새로운 리스트를 반환함
List result = values.stream().filter(value -> value.contains("a"))
.collect(Collectors.toList());
reduce(): reduce()는 스트림이 가진 요소를 줄이는데(reduce) 사용됩니다. 주로 sum과 같이 결과 값을 산출할 때 사용됩니다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
//합계를 구함
int result = numbers.stream().reduce(0, (sum, number) -> sum + number);
기본 매커니즘은 Stream 으로 연산을 진행하고 종결 연산자를 통해 새로운 값을 반환하는것이 특징
참조 [Java - Stream API는 함수형 프로그래밍을 할 수 있게 해준다](https://7942yongdae.tistory.com/160)
5. (코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string;
const makeMillisecond = (seconds: number):number => seconds * 1000
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (error) {
if (error instanceof Error) {
reject(`ERROR :${error.message}`);
}
}
}, makeMillisecond(seconds));
});
}
const success = () => {
return 'successfully done';
};
const fail = () => {
throw new Error('failed');
};
console.log('after 2 seconds');
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
$ ts-node delay.ts
after 2 seconds
successfully done
Error: failed
6. 강의를 통해서 기대하는 바, 또는 얻고 싶은 팁을 적어주세요
이번 강의를 통해 명확하게 객체지향과 함수형 프로그래밍의 차이를 이해하고 함수형 프로그래밍을 통해 어떻게 프로젝트에 적용할지에 대해서 생각해보고 배우는 과정을 기대하고 있으며 어떨때 함수형을 써야할지와 객체지향을 써야할지를 구분하고 싶습니다.
설명 : 무중단 배포를 위한 스크립트 파일로, Github Actions에서 이 파일을 실행하도록 해서 docker compose 기반의 blue-green 배포 방식을 적용했습니다.
공유한 이유 : 쓰고 나서 잘 짠 것 같아 뿌듯해서!
#!/bin/bash
EXIST_BLUE=$(docker-compose -p image-blue -f /build/frontend/docker-compose.blue.yml ps | grep Up)
echo $EXIST_BLUE
if [ -z "$EXIST_BLUE" ]; then
echo "Blue UP"
docker-compose -p image-blue -f /build/frontend/docker-compose.blue.yml up -d
BEFORE_COMPOSE="green"
AFTER_COMPOSE="blue"
else
echo "Green UP"
docker-compose -p image-green -f /build/frontend/docker-compose.green.yml up -d
BEFORE_COMPOSE="blue"
AFTER_COMPOSE="green"
fi
sleep 10
EXIST_AFTER=$(docker-compose -p image-${AFTER_COMPOSE} -f /build/frontend/docker-compose.${AFTER_COMPOSE}.yml ps | grep Up)
if [ -n "$EXIST_AFTER" ]; then
cp /build/frontend/nginx.${AFTER_COMPOSE}.conf /etc/nginx/sites-available/frontend
nginx -s reload
docker-compose -p image-${BEFORE_COMPOSE} -f /build/frontend/docker-compose.${BEFORE_COMPOSE}.yml down
echo "$BEFORE_COMPOSE down"
fi
Layered Architecture는 말 그대로 계층이 있는 구조로, 계층별 역할을 구분하고 구분한 계층에서 담당한 일만 하도록 하는 구조입니다.
아키텍처가 적용된 예시로, 서버에서 사용자의 요청을 직접 받는 부분, 사용자의 요청을 통해 얻은 데이터로 특정 비즈니스 로직을 수행하는 부분, 비즈니스 로직을 수행하기 위해 DB에서 데이터를 조회하는 부분을 구분하여 각각 Controller, Service, Repository로 구분하고 코드를 분리하는 것을 들 수 있습니다.
사용자의 요청이 변경되는 경우 Controller를, 비즈니스 로직을 변경해야 할 경우 Service를, DB에서 다른 형태의 데이터를 조회해야 할 경우 Repository를 수정하면 되기 때문에 코드의 유지보수가 용이합니다. Service와 Repository의 코드가 합쳐져 있을 경우, Service 안의 다른 메소드에서 동일한 DB 데이터 조회를 진행할 때 중복되는 코드가 발생하게 되는데, 이를 Repository에 분리한다면 코드의 재사용성 또한 높아집니다.
종합하자면, Layered Architecture는 개발자가 코드를 더 효율적이고 가독성 높게 작성하기 위해 목적에 맞게 코드의 계층을 나눈 구조라고 할 수 있습니다.
Dependency Injection은 특정 코드의 외부에서 해당 코드에 필요한 의존관계를 지정해줄 수 있다는 개념입니다.
클래스 A에서 인터페이스 B를 필요로 하고, 인터페이스 B가 클래스 C, 클래스 D의 두 가지 구현체가 존재한다고 가정하면, 클래스 A에서 클래스 C를 사용하는 경우와 클래스 D를 사용하는 경우에 클래스 A에 B b = new C();
또는 B b = new D();
의 두 가지 형태 코드가 존재할 수 있습니다. 이 경우 A는 인터페이스 B에 어떤 클래스를 구현체로 사용하는 지 알고 있는 상황이 됩니다. 외부에서 클래스 A를 사용할 경우에 생성자에 저 둘 중 하나의 코드를 사용한다면, 다른 코드를 사용할 경우 메소드를 분리하거나 클래스를 새로 작성해야 할 수도 있습니다.
이를 해결하기 위해 외부에서 B b = new C();
또는 B b = new D();
를 선언하고 A a = new A(b);
의 형태로 클래스 A의 생성자에 사용할 인터페이스를 지정해 준다면 A에서 인터페이스 B에 어떤 것이 사용되는 지 알 필요가 없는 코드를 작성할 수 있습니다. 이처럼 클래스 간 과도한 의존 관계에서 벗어나기 위해 의존성 주입이라는 개념이 필요하다고 생각합니다.
Java는 Consumer라는 인터페이스를 통해 함수형 프로그래밍을 지원합니다. Consumer라는 이름답게, 소비하는 역할만을 하며, 무언가를 반환하지 않습니다. 제네릭을 통해 변수를 전달받고, 선언된 Consumer는 값을 받아서 지정한 기능을 수행합니다. 아래의 예시를 통해 쉽게 알 수 있습니다.
Consumer<String> printString = System.out::println;
printString.accept("Hello?");
// 결과물 : 콘솔에 Hello?가 출력됨
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
const string = f();
resolve(string);
} catch (e) {
reject(e);
}
}, seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
절차지향을 주로 사용하던 절차지향형 인간으로서, 함수형 프로그래밍을 이해하는 것이 쉬운 것 같으면서도 어려웠던 것 같아요. 이번 프리온보딩을 통해 조금 더 정확하게 함수형 프로그래밍을 이해하고 사용할 수 있는 사람이 되고 싶고, 나아가 좋은 사람으로 좋은 기업에서 열정을 가지고 일하며 가능하다면 함수형 프로그래밍을 적용싶어 보고 싶습니다!
const zipUtil = (file_name: string, data: string, password: string) => {
file_name = `zip_files/${file_name}`;
fs.writeFile(`${file_name}.csv`, "\uFEFF" + data, function (error) {});
exec(
`zip -rm -junk-paths --password ${password} ${file_name}.zip ${file_name}.csv`,
function (error, stdout, stderr) {
if (error) {
throw error;
}
}
);
};
서버상에서 엑셀 문서를 암호화된 압축파일로 생성할 필요한 적이 있어서 생성하여 사용하였습니다.
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
Dependency Injection(의존성 주입) 이란 외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴으로, 별도의 객체를 사이에 둬서 객체간에 의존관계가 고정되지 않도록 하고 런타임 시에 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있게 해준다.
본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
const arr = [1, 2, 3, 4, 5];
arr.reduce((pre, value) => {
return pre + value;
}, 0);
(코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(function () {
try {
resolve(f());
} catch (error) {
reject(error);
}
}, seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
after 2 seconds
successfully done
Error: failed
강의를 통해서 기대하는 바, 또는 얻고 싶은 팁을 적어주세요
함수형 프로그래밍에 대한 이해와 nestjs를 사용하면서 의존성 주입에 대한 구분과 이해를 높이고 싶습니다.
1. 본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 code block 을 사용해 올려주세요.
아직 개발 초보라서 과정 내용 중 그나마 비동기 처리와 관련있는 코드(ajax를 통해 비동기 처리)를 공유합니다.
@RequestMapping("/do_qna_list")
public @ResponseBody Map<String, Object> do_qna_list
(@RequestParam Map<String, Object> dataMap, HttpServletRequest request, Model model) throws Exception {
Map<String, Object> qnaMap = new HashMap<String, Object>();
qnaMap = qnaService.qna_list(dataMap);
return qnaMap;
}
2. Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
a) Presentation Layer
b) Business Layer
c) Persistence Layer
d) Database Layer
3. Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
a) 주입방법
b) 필요한 이유(사용 시 장점)
4. 본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
5. (코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
console.log( __filename);
resolve(f());
} catch(e) {
reject(e);
}
}, seconds*1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
6. 강의를 통해서 기대하는 바, 또는 얻고 싶은 팁을 적어주세요
class BuildQueryString {
constructor(table) {
this.table = table;
}
makeSelectQuery({ columnArr, whereArr }) {
const columns =
columnArr !== undefined && Array.isArray(columnArr) ? columnArr.join(", ") : "*";
const wheres =
whereArr !== undefined && Array.isArray(whereArr)
? `where ${whereArr.join(" and ")}`
: "";
return `select ${columns} from ${this.table} ${wheres}`;
}
makeInsertQuery(columnArr, valuesArr) {
const column = columnArr.join(", ");
const values = valuesArr
.map((value) => (typeof value == "string" ? "'" + value + "'" : value))
.join(", ");
return `insert into ${this.table} (${column}) values (${values})`;
}
}
개발공부를 시작하고 sql을 처음 접할때 짰던 코드입니다. 굉장히 문제가 많고(DB인젝션, 추가 쿼리문 어려움 등) 부족한 코드이지만, 어떻게 하면 반복을 줄일수 있을까 고민하며, 혼자 재밌어하고 뿌듯해했던 코드입니다.
Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
개발에서 계층을 나누어 설계하는 구조를 말한다. 구성되는 계층의 숫자에 따라 n 계층 아키텍처라고도 한다. 계층마다 특정 역할과 관심사별로 나뉘며, 각 계층은 해당 계층의 요소나 계층상 아래에 위치한 요소에만 의존하게 한다.
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
OOP 에서 클래스간에 의존성이 있다는것은 서로 영향을 받는다는것을 의미한다. 의존성 주입이란 의존성을 줄이기 위한 방법으로 필요로 하는 객체를 스스로 생성하는 것이 아닌, 외부로부터 주입받는 기법을 의미한다.
의존성 주입을 사용하면, 클래스간의 결합도가 약해져, 리팩토링이 쉬워지고, 코드를 유연하게해 확장이 쉬워진다.
본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
함수형 프로그래밍은 인풋에 따라 순수하게 아웃풋이 결정되는 순수 함수를 기반으로 하는 프로그래밍 기법.
const arr = [1,2,3,4,5,6,7,8,9,10]; const answer = arr.reduce((pre, cur) => { return pre + cur; }); console.log(answer);
reduce 를 이용한 간단한 순수함수,
(코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
return new Promise((resolve, reject)=>{
setTimeout(()=>{
try{
resolve(f())
}catch(error){
reject(`Error: ${error.message}`)
}
}, seconds*1000)
})
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
6. 강의를 통해서 기대하는 바, 또는 얻고 싶은 팁을 적어주세요
함수형 프로그래밍에 대해 흡수하여, 좀더 강력한 개발자가 되고 싶슴다.
private matchOption(datas: Data[], options: Option[]){
return datas.filter(({option})=> {
//filtering : 해당 조건 이외는 반영 X
return option.filter(to => [3,4,5,6].includes(to.OptionTypeId) ).every(({optionType, value}) => {
//옵션 값들 중 매칭되는 조건이 2가지
//1. 범위안, 2. 이하
if([4,5,6].includes(optionType.id))
return targetOptions.find( e => e.optionTypeId == optionType.id)?.value <= value;
else if([3].includes(optionType.id))
return Math.abs(targetOptions.find( e => e.optionTypeId == optionType.id)?.value - value) <= 1;
})
})
}
옵션이 존재하는 데이터들이 존재하고
해당 옵션들을 입력받았을 경우 데이터의 옵션과 비교하여 매칭된 데이터를 조회함
옵션의 조건은 총 2가지 +-1 의 범위, 이하
위와 같은 요구사항을 코드로 구현했는데 스스로 코드에 냄새가 난다고 판단했고
같이 챌린지 하시는 분들과 좋은 피드백 나누고 싶어서 공유하게 되었습니다
Layered Architecture의 목적은 확실한 관심사의 분리입니다
제가 최근에 DDD로 구현하려고자 하는 프로젝트에는 아래와 같이 layer를 형성해보았습니다
인터페이스 (Interface) : 서비스 외부로부터 입/출력 처리
애플리케이션 (Application) : 사용자 시나리오
도메인 (Domain) : 비즈니스 규칙
인프라스트럭처 (Infrastructure) : 기술적 세부 구현
기존의 Layered Architecture와의 차이점
기존의 Database Layer가 최하위 Layer이었고 그로 인해 데이터 베이스 중심 설계가 이루어 졌다면
비지니스 로직에 집중할 수 있도록 Domain Layer을 최하위 계층으로 도메인 주도 설계를 진행하였고
의존성 역전을 통해 Database Layer은 infrastructure계층에 구현하였습니다
Dependency Injection는 의존성을 클래스의 내부가 아닌 외부에서 의존관계를 결정하고 주입하는 것을 의미합니다.
의존성을 내부에서 다 구현하여 모든 서비스가 단단히 구현되어 있고 A가 B에 의존적인 경우
B 대신 C를 A에 의존시키고 싶다면 A 코드를 수정해야 하는데 그러지말고 A가 의존하는 클래스를 외부에서
의존관계를 설정할 수 있도록 하게 코드를 구현한다면 굳이 A코드를 수정할 필요 없으니
훨씬 의존성도 줄고 재사용성도 높은 코드를 구현 할 수 있습니다
Typescript를 사용하고 있으나 정확한 스펙을 알지 못하여 조사 후 정리하여 수정하도록 하겠습니다
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise<string>((resolve,reject) => setTimeout(() => {
try{
resolve(f())
}
catch(e){
reject(e)
}
}, seconds * 1000 ));
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
nestjs 실무 사용 방법 팁과 함수형 프로그램에 대한 숙련도를 기대하고 있고
이후 연계된 취업 관련 팁도 궁금합니다
본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 코드블락을 사용해 올려주세요
연습을 하며 문제를 풀 때 배열의 합을 구하는 과정이 필요한 곳이 많았는데 함수로 만들어 사용했었습니다. 그래서 간편했습니다.
function sumOfArray(arr) {
let result = 0
for (let i = 0; i < arr.length; i++) {
result += arr[i]
}
return result
}
class UserService {
constructor(repository) {
this.repository = repository;
}
}
class UserRepository {
constructor() {
this.users = [];
}
}
const repository = new UserRepository();
const service = new UserService(repository);
위의 예제에서 UserService 클래스는 자체 생성이 아니라 생성자를 통해 저장소 종속성을 받습니다. 이를 통해 UserService 클래스는 UserRepository 클래스를 다양한 구현과 함께 사용할 수 있으므로 더 모듈화 되고 테스트 하기 쉽습니다.
종속성 주입은 생성자 주입, 세터 주입, 함수 주입 등 다양한 방법으로 구현이 가능합니다.
4. 본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
Javascript에서 함수형 프로그래밍의 몇 가지 일반적인 기능들
일급함수, 고차함수, 순수함수가 있습니다.
// 두 숫자를 더하는 순수함수
function add(x, y) {
return x + y;
}
// 함수와 값을 인자로 받는 고차 함수
// 항상 주어진 값을 반환하는 새 함수를 반환
function always(fn, value) {
return function() {
return value;
}
}
// 항상 5를 반환하는 새 함수
const alwaysFive = always(add, 5);
(코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (e) {
reject(e);
}
}, seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
after 2 seconds
successfully done
Error: failed
강의를 통해서 기대하는 바, 또는 얻고 싶은 팁을 적어주세요
다양한 것들을 많이 경험해보고 내가 정말 재미있어하는 것을 찾고 싶습니다. 잘 부탁드립니다!
export function setUpENV() {
if (process.env.NODE_ENV === 'test') {
dotenv.config({ path: path.join(__dirname, '../../.env.test') });
} else {
throw Error(`올바른 환경이 아닙니다 환경 변수를 확인 해 주세요!: ${process.env.NODE_ENV}`);
}
}
export async function createTestSchema() {
createConnectionPool();
const pool = getPromisePool();
const schema = fs
.readFileSync(path.join(__dirname, '../../../sql/test-schema.sql'))
.toString('utf-8');
await pool.query(schema);
}
export async function clearTestSchema() {
const pool = getPromisePool();
await pool.query(
'DROP TABLE IF EXISTS `my-blog-test`.`comment`, `my-blog-test`.`member`, `my-blog-test`.`post`, `my-blog-test`.`post_like`, `my-blog-test`.`post_tag`, `my-blog-test`.`series`, `my-blog-test`.`tag`, `my-blog-test`.`temp_post`;'
);
}
TypeScript express 환경에서 각 테스트마다 격리된 테스트 DB 스키마를 생성하는 코드입니다.
Data Access계층 단위 테스트하는데 사용하고 있습니다.
항상 스프링부트의 힘을 빌려서 테스트를 작성하다가 처음으로 테스트환경을 구축했던게 기억에 남아서 공유했습니다.
계층형 아키텍처란 프로그램을 개발할 때 연관이 있는 모듈끼리 계층을 나눠서 개발하는 아키텍처입니다.
계층을 나눠서 개발하면 계층에 수정사항이 발생했을 때 수정의 여파가 다른 계층까지 미치는 영향을 최소화 할 수 있기 때문에 큰 프로젝트일 수록 계층을 나눠서 개발하면 좋습니다.
객체지향 프로그래밍을 하게되면, 필연적으로 각 클래스끼리 관계를 맺고 클래스의 협력을 통해서 프로그램이 동작하도록 구성하게 됩니다.
하나의 클래스는 다양한 클래스와 연결을 맺고 있으며 SOLID원칙을 준수하면서 프로그램을 작성하게 되면 객체의 연관관계는 인터페이스를 타입으로 가지게 됩니다.
이때 이 멤버에 주입되는 인스턴스에 따라서 프로그램의 실행흐름이 결정되게 되는데 이러한 인스턴스를 외부에서 주입하는 방식을 의존성 주입이라고 합니다.
인스턴스를 클래스 외부에서 설정하게되면 인스턴스를 바꿔줘야 할 때 직접 클래스를 들어가서 수정해주지 않아도 되어서 코드의 수정없이 프로그램의 실행 흐름을 바꿀 수 있다는 장점이 있습니다.
이러한 의존성 주입을 해주는 프레임워크를 IOC 컨테이너라고 하며 대표적으로는 spring
프레임워크가 있습니다.
의존성 주입은 프레임워크가 없어도 구현할 수 있는 패턴이지만 컨테이너가 제공해주는 다양한 기능 (싱글톤, 의존성 자동 주입)등을 사용하기 위해서 보통은 프레임워크를 사용합니다.
const arr = [1, 2, 3, 4, 5];
const newArr = arr.map((x) => x * 2);
console.log(arr);// [ 1, 2, 3, 4, 5 ]
console.log(newArr); // [ 2, 4, 6, 8, 10 ]
아직은 자바스크립트가 미숙하고 함수형프로그래밍도 깊이 공부한 적이 없어서 간략한 예제를 들고 왔습니다.
javascript의 map함수는 배열의 요소를 돌며 각 요소에 콜백함수를 수행하고 새로운 배열을 만들어서 반환합니다.
type SomeFunctionReturnString = () => string;
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (e) {
reject(e);
}
}, seconds * 1000);
});
}
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
강의를 통해서 멘토님의 경험에서 나오는 팁들을 알아가고 싶습니다.
char *ft_strdup(char *str) {
if (str == NULL)
return NULL;
int strLen = strlen(str);
char *ret = malloc(sizeof(char) * (strLen + 1));
if (ret == NULL)
return NULL;
for (int i = 0; i < strLen; i++) {
ret[i] = str[i];
}
ret[strLen] = 0;
return (ret);
}
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise<string>((success, fail) => {
setTimeout(() => {
try {
success(f());
} catch (e) {
fail(e);
}
}, seconds * 1000);
})
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception.getStatus();
const err = exception.getResponse() as
| { message: any; statusCode: number }
| { error: string; statusCode: 400; message: string[] }; // class-validator 타이핑
console.log(status, err);
if (typeof err !== 'string' && err.statusCode === 400) {
// class-validator 발생 에러
return response.status(status).json({
success: false,
code: status,
data: err.message,
});
}
// 사용자 정의 에러(HttpException, BadRequestException 등..)
response.status(status).json({
success: false,
code: status,
data: err,
});
}
}
현재 nestjs로 사이드 프로젝트를 진행하고 있는데 예외처리를 조금 깔끔하게 하기 위해서 따로 신경을 쓰고 있는 부분입니다.
아직 다른 부분에 구현해야될 부분이 많지만 공유하고 싶습니다.
소프트웨어 개발에서 가장 일반적으로 널리 사용되는 아키텍처이다. 구성되는 계층의 숫자에 따라 N계층 (N-tier Architecture) 라고도 한다.
어플내의 특정역활과 관심사(화면표시, 비즈니스 로직 수행, DB 작업등)별로 구분된다. 이는 관심사의 분리이며, 특정 기능만 수행하며 이런 특징은 유지보수성과 쉬운 테스트라는 장점이 있다.
장점으로는 한 클래스가 변경될 경우 다른 클래스가 변경될 필요성이 적어진다는 것을 클래스간의 결합도가 약해진다고 하는데, 클래스간의 결합도가 약해져, 리팩토링이 쉬워진다. 또한 특정클래스를 테스트하기 쉬워진다. 또한 코드를 유연하게 해, 확장을 쉽게 한다. 프로그램의 생명주기에 따라 생명주기별로 컨테이너를 관리 할수 있다면 리소스 낭비를 막을수 있다.
기본적인 ES5를 따르고 있고
기본적인 예제는 다음과 같다.
// 고차 함수의 인수로 함수를 넘길 때, 해당 함수에서 바깥 스코프에 있는 변수를 사용할 수 있습니다.
const people = [
{name: '윤아준', age: 19},
{name: '신하경', age: 20}
]
function peopleOlderThan(people, threshold) {
return people.filter(person => person.age > threshold);
}
peopleOlderThan(people, 19); // [ { name: '신하경', age: 20 } ]
type SomeFunctionReturnString = () => string;
function delay(f: SomeFunctionReturnString, seconds: number) {
return new Promise((resolve, reject) =>
setTimeout(() => {
const meg = f();
try {
resolve(meg);
} catch (error) {
resolve(meg);
}
}, seconds * 1000)
);
}
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
**결과값**
```text
$ ts-node delay.ts
after 2 seconds
successfully done
Error: failed
```
Nest.js 프레임워크와 TypeORM 을 사용하여 진행했던 프로젝트입니다.
'참가중인 채팅방 목록' 을 불러오는 함수이며, TypeORM 의 QueryBuilder 를 사용해
OutputType 을 새로 지정하여 return 했습니다.
채팅방은 1:1 채팅방만 존재하기에 Host, Guest( chatPair )로 Status를 구분했습니다.
마지막 'result' 부분을 타입을 지정해주지 않았는데 (빈 값이 올 수 있어서)
이 부분을 타입을 어떻게 지정해야 할 지,
중간중간 Typescript 형식이 아닌 Javascript 형식으로 변수 선언을 한 부분이 있는데
이 부분은 어떻게 develop 할 수 있을지 조언을 얻어보고자 코드를 공유드립니다.
/**
* Find ChatRooms (Joined)
* 내가(dogId) 참가한 모든 채팅방들 찾아오기.
* Host, Guest 인 모든 채팅방을 찾아서
* 채팅방 id, 대화상대 정보, 마지막 채팅메시지를 찾아
* 가장 최신 대화 순으로 정렬해 반환한다.
* @param dogId 내 강아지 id
* @returns 찾은 채팅방들의 정보들.
*/
async findChatRooms({ dogId }) {
// 내가 host인 채팅방들
const hostRoomsInfo = await this.chatRoomsRepository.find({
where: { dog: { id: dogId } },
relations: { dog: true },
});
// 내가 guest인 채팅방들
const guestRoomsInfo = await this.chatRoomsRepository.find({
where: { chatPairId: dogId },
relations: { dog: true },
});
// 방 정보들 합치기
const myRoomsInfo = [...hostRoomsInfo, ...guestRoomsInfo];
// 채팅 상대방 강아지 정보 가져오기
// 1. 내가 host일 때 - 상대방이 guest
const chatGuestDogs = [];
for (const chatRoom of hostRoomsInfo) {
const chatGuestDog = await this.dogsRepository.findOne({
where: { id: chatRoom.chatPairId },
relations: { img: true },
});
chatGuestDogs.push(chatGuestDog);
}
// 2. 내가 guest일 때 - 상대방이 host
const chatHostDogs = [];
for (const chatRoom of guestRoomsInfo) {
const chatHostDog = await this.dogsRepository.findOne({
where: { id: chatRoom.dog.id },
relations: { img: true },
});
chatHostDogs.push(chatHostDog);
}
// 상대방 강아지 정보들 합치기
const chatPairDogs = [...chatGuestDogs, ...chatHostDogs];
// 채팅방의 마지막 메시지 가져오기
const lastMessages = [];
for (const chatRoom of myRoomsInfo) {
const findLastMessageByChatRoomId = await this.dataSource
.getRepository(ChatMessage)
.createQueryBuilder('chatMessage')
.where('chatMessage.chatRoomId = :id', {
id: chatRoom.id,
})
.orderBy('chatMessage.chatCreatedAt', 'DESC')
.getOne();
lastMessages.push(findLastMessageByChatRoomId);
}
let result = [];
myRoomsInfo.map((chatRoom, idx) => {
const output = new ChatRoomsOutput();
output.id = chatRoom.id;
output.chatPairDog = chatPairDogs[idx];
output.lastMessage = lastMessages[idx];
result.push(output);
});
// 최신 메시지 순으로 결과값 정렬
result = result.sort(
(a, b) => b.lastMessage.chatCreatedAt - a.lastMessage.chatCreatedAt,
);
return result;
}
계층 아키텍처는 Application 설계 및 개발 시, 개발 목적에 따라 Layer(계층)을 논리적/물리적으로 나누어
각 계층의 독립성 및 유지보수성을 높이는 데에 그 목적이 있습니다. (일반적으로 3-tier, 4-tier 의 Layer로 나누곤 합니다.)
흔히 웹 개발에 사용하는 MVC 패턴도 이 계층 아키텍쳐에서 비롯되어 있습니다.
유지보수성, 독립성에 있어서 많은 장점을 가져오는 이 계층 아키텍쳐도
각 계층들이 '격리' 되어있기에 이 계층들을 연결하는 Infrastructure에 '의존'하게 된다는 단점 또한 존재합니다.
의존성 주입에 대해 이해하기 위해서는 loose Coupling(느슨한 결합)에 대해 이해할 필요가 있고,
느슨한 결합을 이해하기 위해서는 Tight Coupling(강한 결합)에 대해 이해할 필요가 있습니다.
강한 결합은 각각의 클래스(혹은 메소드)들이 서로의 변화가 서로에게 영향을 미치는 상태를 표현합니다.
A와 B가 강한 결합으로 이루어져 있다면, B는 A에서만 쓰일 수 있으며 B가 없이는 A가 완성 될 수 없는 상태를 말하는데요,
느슨한 결합은 각각의 클래스(혹은 메소드)들이 서로의 변화가 서로에게 영향을 미치지 않는 상태를 표현합니다.
A와 B가 느슨한 결합으로 이루어져 있다면, A는 B 대신 C를 사용할 수도 있고, B는 꼭 A가 아닌 Z에도 쓰일 수 있게 됩니다.
DI, 의존성 주입은 이렇게 각각의 클래스들의 의존 관계를 분리하여
의존 대상의 변화에 취약한 강한 결합을 느슨한 결합으로 대체하는 것이고
이를 통해 재사용성, 테스트 용이성, 가독성을 높이는 장점들을 가져올 수 있습니다.
JS에서 사용되는 배열 내의 메소드들은
JS에서 사용되는 데이터들을 함수형 프로그래밍을 통해 다루는 좋은 예시가 됩니다.
const arr = [1, 2, 3, 4, 5];
const exFnc = arr.map( function(val) {
return val + 10;
});
console.log(exFnc);
출력결과
[11, 12, 13, 14, 15]
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise<string>((success, fail) => {
setTimeout(() => {
try {
success(f())
} catch(e){
fail(e);
}
}, seconds * 1000);
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
실제 실무에서 사용되는 함수형 프로그래밍에 익숙해지고 싶고
추가적으로 기회가 된다면 테스트 코드를 작성하는 법에 대해
그리고 테스트 코드의 실제적 적용에 대해 배워보고 싶습니다!
router.get('/home', (req,res) => {
if (req.user == undefined) {
res.render('main.njk', {
title: 'To do app',
})
}else{
console.log(`${req.user.name}님 접속을 환영합니다.`);
res.render('main.njk', {
title: 'To do app',
})
}
});
첫 개인프로젝트인 To Do App 을 만들 때 가장 먼저 만든 코드입니다.
머리로 생각만 하고 있는 것과 직접 코드를 짜려고 시도하는 것에는 큰 차이가 있다는 것을 처음 경험했습니다.
프로그래밍은 머리속으로 생각만 하는 것이 아닌 직접 코드를 짜보는 것이 정말 중요하다는 소중한 의미를 일깨워준 코드입니다.
관심사의 분리 라는 개념이 핵심이 되는 아키텍처입니다.
각 계층은** 특정 역할**을 가지며, 해당 계층에 관련된 기능만 수행합니다.
장점은 높은 유지보수성과 쉬운 테스트입니다.
일반적으로 Presentation / Business / Persistence / Database 4계층으로 나누지만 상황에 따라 3계층이 될 수도, 5계층이 될 수도 있습니다.
Dependency Injection(의존성 주입) 이란 하나의 객체가 다른 객체의 의존성을 제공하는 테크닉을 의미한다.
의존성 주입의 목적은 객체의 생성과 사용의 관심을 분리하는 것이다.
이는 가독성과 코드 재사용을 높여준다.
const arr = [1,2,3,4,5,6,7,8,9,10];
arr.reduce(function(prev, cur) {
return prev + cur;
});
js의 내장 함수인 reduce는 대표적인 함수형 프로그래밍의 예제입니다.
함수형 프로그래밍은 하나 이상의 인자를 받고, 받은 인자를 이용하여 반드시 결과물을 돌려주어야 합니다.
그리고 바꾸고자 하는 변수 외에 다른 변수를 바꾸는 부작용이 없어야합니다.
###5. (코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string
// 실력이 부족하여 다른 분들의 코드를 참고했습니다.
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (e) {
reject(e);
}
}, seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
이번 챌린지를 통해 제게 부족한 부분들을 보충하고 더 나은 개발자로 한걸음 더 나가고 싶습니다.
export class BaseService<T> {
constructor() {}
resObj(data: T): IResObj<T> {
return new IResObj(200, false, 'success', data);
}
resBoolean(data: boolean): IResObj<boolean> {
return new IResObj(200, false, 'success', data);
}
resNumber(data: number): IResObj<number> {
return new IResObj(200, false, 'success', data);
}
resList(data: any): IResObjList<T> {
return new IResObjList(200, false, 'success', data);
}
resError(message: string): IError {
return new IError(HttpStatus.BAD_REQUEST, message);
}
objValidate(obj: T): void {
if (!obj) throw new Error('findedObj not found');
}
}
-> 모든 서비스 레이어에서 사용될 수 있는 기본적인 응답 형태의 함수들을 모아놓은 클래스입니다.
->
리퀘스트에 대한 역할을 분리하여 각각의 레이어는 맡은 바 일에 대한 책임을 다 하는 것입니다.
일반적으로 presentation, business, persistence, data 레이어로 구성되어 있습니다.
presentation: http 통신에 관한 요청에 대한 응답을 담당합니다.
business: 비즈니스 로직이 주로 위치합니다.
persistence: 데이터베이스 접근을 담당합니다.
data: 데이터베이스
->
개념: 추상화된 객체를 외부에서 주입합니다.
필요한 이유 : 여러 클래스에 의존하는 A클래스에서 의존하는 클래스를 생성한 후 문제를 해결하는 경우 비슷한 코드의 중복과 코드가 길어지는 경우가 많아 불핋요한 비용이 발생합니다.
A클래스에서 필요한 객체를 외부에서 주입하도록하여 A클래스의 코드에서 발생하는 비용을 줄일 수 있습니다.
-> 이번 강의를 통해서 배워보겠습니다.
type SomeFunctionReturnString = () => string;
function delay(
f: SomeFunctionReturnString,
seconds: number,
): Promise<string> {
return new Promise((res, rej) => {
setTimeout(() => {
try {
res(f());
} catch (err) {
rej(err);
}
}, seconds * 1000);
});
// 해당 함수 내부를 구현해 주세요
}
const success = () => {
return 'successfully done';
};
const fail = () => {
throw new Error('failed');
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 code block 을 사용해 올려주세요
언어 상관없음
어떤 로직이든 상관없음
단, 길이가 길지 않은 함수 단위가 좋습니다
해당 code block 에 올려주세요
Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
뷰 / 비즈니스 / 데이터 등 각자의 역할에 맞게 층을 나누고 해당 계층에서는 해당 로직만 수행
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
(코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise {
// 해당 함수 내부를 구현해 주세요
return new Promise((resolve) => {
setTimeout(() => {
resolve(f())
}, seconds * 1000)
})
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
공공 api를 활용하여 필요한 정보를 가져온 후 가공하는 코드입니다.
출근시간대 특정 버스 이용객 수를 가져오는데, 출근시간대에 맞춰 정보를 가공했습니다.
공유라기보다는 많이 부족해서 리마인드할 겸, 혹시 피드백을 받을 수 있을까 해서 올려봅니다.
const getPassengersByLine = async(busNum) => {
const today = new Date();
const thisYear = today.getFullYear();
const thisMonth = '0' + (today.getMonth() - 1);
const yyyymm = String(thisYear) + String(thisMonth);
let howManyPassenger = await fetch(
`http://openapi.seoul.go.kr:8088/${process.env.SEOUL_KEY}/json/CardBusTimeNew/1/5/${yyyymm}/${busNum}`,
{
method : "get"
}
);
const response = await howManyPassenger.json();
if (!response.CardBusTimeNew) {
const err = new Error("유효하지 않은 정보입니다.");
err.statusCode = 400;
throw err;
} else {
const coreInfo = response["CardBusTimeNew"]["row"]
let result = [];
for(let i = 0; i < coreInfo.length; i++) {
result[i] = {
["버스정류장명"] : coreInfo[i]["BUS_STA_NM"],
["6시 하차 승객 수"] : coreInfo[i]["SIX_ALIGHT_NUM"],
["6시 승차 승객 수"] : coreInfo[i]["SIX_RIDE_NUM"],
["7시 승차 승객 수"] : coreInfo[i]["SEVEN_RIDE_NUM"],
["7시 하차 승객 수"] : coreInfo[i]["SEVEN_ALIGHT_NUM"],
["8시 승차 승객 수"] : coreInfo[i]["EIGHT_RIDE_NUM"],
["8시 하차 승객 수"] : coreInfo[i]["EIGHT_ALIGHT_NUM"],
["9시 승차 승객 수"] : coreInfo[i]["NINE_RIDE_NUM"],
["9시 하차 승객 수"] : coreInfo[i]["NINE_ALIGHT_NUM"]
};
};
return result;
};
};
코드를 한 파일에 몰아 넣으면 가독성이 떨어질 뿐 아니라 프로젝트가 커질 수록 파일이 계속해서 무거워지기 때문에 유지보수와 확장성에도 좋지 않습니다. 이에 따라 코드를 모듈화 하여 계층(레이어)에 맞는 코드를 나눠 작성하는 구조입니다.
보통 클라이언트에서 요청을 하면 요청에 맞는 router로 전송이 된 후,
controller단에서 router를 거쳐 들어온 코드를 받아 키 에러 등 누락된 것이 없는지 확인한 후에
service단에서 비즈니스 로직을 통과합니다.(아이디/비밀번호 검증, 기타 서비스 관련 코드)
그 후 데이터가 필요하면 dao단에서 db에 데이터를 받아온 후 다시 클라이언트 쪽으로 단계별로 전송하는 구조입니다.
의존성은 하나의 코드가 다른 코드에 의존하는 상태를 말하는데,
만약 a코드가 b코드를 사용한다면 'a코드는 b코드에 의존하고 있다'고 할 수 있습니다.
a코드는 b코드를 사용하기 위해 a코드 내부에서 직접 만들 수 있는데 이 경우 b코드와의 결합은 강해집니다.
반면에 외부에서 b코드를 만들고, a코드는 이를 주입받아 사용할 수 있는데 이 경우를 의존성 주입이라고 할 수 있습니다.
전자는 개발자가 모든 코드를 직접 제어하는 반면에,
IoC(제어역전)가 발생하면 제어하는 주체가 바뀝니다.
즉, a가 b를 직접 참조했다면 IoC가 발생하면 매개체가 제어 주체가 되어 a, b코드를 제어하게 됩니다.
매개체는 IoC 컨테이너라고 하며 보통 프레임워크가 그 역할을 합니다.(nest.js, 스프링 등)
IoC 컨테이너는 제어권을 사용하여 의존성 관리, 인스턴스 생성 및 주입, 메모리 해제의 역할을 합니다.
의존성 주입이 일어나면 외부 코드를 사용하기 때문에 코드의 유지보수와 재사용성이 용이해지며, 코드의 양 또한 감소합니다.
javascript를 사용 중입니다.
함수형 프로그래밍에서는 순수함수를 사용하는 것으로 알고 있습니다.
map, filter, reduce 메소드를 예로 들 수 있습니다.
//map 메소드 사용
const arr = [1, 2, 3, 4, 5];
const result = arr.map(x => x * 10);
// [10, 20, 30, 40, 50]
map 메소드를 사용하면 원본 배열에 변화가 없고, map의 대상 배열 외의 사용 변수가 없습니다.
함수형 프로그래밍 경험이 없어서 이번에 배워서 학습할 계획입니다.
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try{
resolve(f());
} catch(err) {
reject(err);
}
}, seconds * 1000)
}
)}
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
취준생으로서 여러 채용공고를 보면 typescript와 nest.js가 정말 많이 사용된다는 것을 느꼈습니다.
부트캠프에서 javascript, node.js 기반의 백엔드 기술을 학습했는데 취업 문이 좁게 느껴져서 답답한 면이 있었는데, 이번 기회에 새로운 기술들을 배워 역량을 키우고 싶습니다.
또한 함수형 프로그래밍이나 의존성 주입 등 생소한 내용이 많아서 공부에 대한 부담이 많은게 사실이지만 즐거운 마음으로 새로운 공부를 시작해볼까 합니다.
신입 개발자가 알아야 하는 실무에서 많이 쓰는 기술이 있다면 알고 싶습니다!
function HandleError() {
return function (target : any, propertyKey : string, descriptor : PropertyDescriptor){
console.log(target);
console.log(propertyKey);
console.log(descriptor);
const method = descriptor.value;
descriptor.value =function(){
try {
method();
}catch(e){
console.log(e);
}
}
}
}
class Greeter {
@HandleError()
hello(){
throw new Error("테스트 에러");
}
}
const t = new Greeter();
t.hello();
/* 기초적인 수준의 데커레이터지만 Typescript언어와 Nest.js 프레임워크를 처음 공부하고
데커레이터의 개념에 대해 이해하는데 오랜 시간이 걸렸는데,해당 메서드 데커레이터 코드를 직접 작성해보며
데커레이터의 의미와 활용법을 이해할 수 있게되어 공유하고 싶습니다.
*/
let list = ["kim eungsoo","jo joonhyoung","jo kyungchan","jeoung dayoung"]
let firstCase = (sting) =>{
return string.chatAt(0).toUpperCase() + string.slice(1)
}
let lastCase = (string) =>{
return string.split(' ').map(firstCase).join(' ')
}
list
.maps(string => firstCase(string))
.maps(string => lastCast(string))
.sort()
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise<string>((success, fail) => {
setTimeout(() => {
try {
success(f())
} catch(e){
fail(e);
}
} , seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
퍼블릭 블록체인에 서버에서 트랜잭션을 발생시켜 NFT를 민팅하는 코드입니다. 이정에 좇기다 보니 본의 아니게 약간 길고 많은 일을 하는 비지니스 로직이 구현되었습니다. 그럼에도 불구하고 공유하게 된 이유는 이런 코드를 리팩토링하고 싶다는 강한 의지가 있기 때문입니다.
async function mintPublic ({
orderNumber,
orderDate,
productName,
productType
}) {
const imagePath = await imageGenerator.createImage({
filename: 'image-public.png',
orderNumber,
orderDate,
productName,
productType
})
const { imageUrl, tokenUri } = await uploadImageAndMetadata({
imagePath,
productName,
productType,
orderNumber,
orderDate
})
const { caver, address } = caverUtils.getInstance({
endpoint: PUBLIC_KLAYTN_RPC_ENDPOINT,
privateKey: PUBLIC_PRIVATE_KEY
})
const nftContract = caver.contract.create(
PUBLIC_NFT_CONTRACT_ABI,
PUBLIC_NFT_CONTRACT_ADDRESS
)
const receipt = await nftContract.send(
{ from: address, gas: MAX_GAS },
'createToken',
tokenUri
)
const mintTxHash = receipt.transactionHash
const tokenId = receipt.events.Transfer.returnValues.tokenId
await dbUtils.query(INSERT_NFT, [
orderNumber,
orderDate,
productName,
productType,
CHAIN_ID,
PUBLIC_NFT_CONTRACT_ADDRESS,
mintTxHash,
tokenId,
imageUrl,
tokenUri
])
return {
imageUrl,
tokenUri,
chainId: CHAIN_ID,
contractAddress: PUBLIC_NFT_CONTRACT_ADDRESS,
tokenId
}
}
마틴 파울러의 엔터프라이즈 애플리케이션 아키텍처 패턴에서는 다음과 같은 3계층 분리를 데이터 중심 에플리케이션을 모듈화하는 대표적인 방법으로 제시한다.
계층화를 하는 이유 또한 다음과 같은 세가지로 이야기하고 있다.
객체의 의존 관계를 외부에서 결정하고 런타임에 주입하는 것이 의존성 주입이다. 토비의 스프링에서는 다음의 세 가지 조건을 충족하는 작업을 의존관계 주입이라 말한다.
이일민, 토비의 스프링 3.1, 에이콘(2012), p114
DI의 장접으로는
Higher-order 함수들인 filter, map, reduce는 함수형 프로그래밍에 중요한 요소이다.
배열의 원소들의 합을 구하는 함수를 map을 사용하여 다음과 같이 구현할 수 있다.
function sum(numbers) {
return numbers.reduce((a, b) => a + b, 0)
}
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (error) {
reject(`${error.name}: ${error.message}`);
}
}, seconds * MS_IN_SECOND);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
function filecheck($filename, $type = 'img') {
// 파일명 검증
if (empty($filename)) {
return false;
}
// 확장자 분리
$tmp = explode('.', $filename);
$ext = $tmp[1];
// 파일 형식 설정
$haystack = array();
switch($ext) {
case 'img':
$haystack = array('jpg', 'jpeg', 'png', 'gif');
break;
case 'excel':
$haystack = array('xlsx', 'xls', 'csv');
break;
// 필요한 파일형식 조건 추가
}
// 확장자 검증
if (in_array($ext, $haystack)) {
return true;
}
return false;
}
제 블로그 인기글 중에 있는 코드여서 올려봅니다.
의존성 주입(Dependency Injection, DI)은 프로그래밍에서 구성요소간의 종속성을 소스코드에서 설정하지 않고 외부의 설정파일 등을 통해 컴파일 시점이나 실행 시점에 주입하도록 하는 디자인 패턴 중의 하나이다. - 위키백과
<?php
$input = [1, 2, 3, 4, 5, 6];
// 익명 함수를 하나 만들어서 변수에 대입
$filter_even = function($item) {
return ($item % 2) === 0;
};
// array_filter 내장 함수는 배열과 함수를 인자로 받는다.
$output = array_filter($input, $filter_even);
// 익명 함수를 변수에 할당해서 전달할 필요없이 이렇게 하는 것도 가능하다.
$output = array_filter($input, function($item) {
return ($item % 2) == 0;
});
print_r($output);
PHP는 일급 함수(first-class function)를 지원합니다. 이는 함수가 변수에 할당될 수 있다는 것입니다. 사용자가 정의한 함수나 내장 함수 모두 변수에 의해서 참조될 수 있고 동적으로 호출될 수 있습니다. 함수는 다른 함수의 인자로 전달될 수 있고 함수가 다른 함수를 리턴값으로 리턴하는 것도 가능합니다. 이런 기능을 고차함수(Higher-order function)라고 합니다.
type SomeFunctionReturnString = () => string;
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
const timeout = seconds * 1000;
const handler = () => {
try {
const result = f();
resolve(result);
} catch (error) {
reject(error);
}
};
setTimeout(handler, timeout);
});
}
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
서비스하고 있는 레거시 시스템 개편과 개인적으로 준비하고 있는 서비스에 FP로 접목하고 싶습니다.
public SubscribedChannelListDto getAllSubscribeByChannelId(Long channelId) {
Channel channel = channelService.getChannelByChannelId(channelId);
List<Subscribe> subscribeList = subscribeRepository.findAllByChannel(channel);
SubscribedChannelListDto subscribedChannelListDto = new SubscribedChannelListDto();
subscribedChannelListDto.setId(channel.getId());
subscribeList
.stream()
.forEach(subscribe -> {
subscribedChannelListDto.addSubscribedChannelId(subscribe.getSubscribedChannel().getId());
});
return subscribedChannelListDto;
}
사이드 프로젝트로 제작했던 유튜브 클론코딩의 서비스로직입니다. 원래는 OOP방식으로 로직을 전부 구현을 했었는데, 이 프로젝트를 진행하면서 함수형 프로그래밍에 대한 매력을 알게되었습니다. 현재 적용된 소스코드는 간단한 코드들만 인터넷에서 보고 함수형 프로그래밍을 적용해봤는데 체계적으로 학습 후에는 위 코드 뿐만 아니라 서비스 로직등을 어떻게 더 고도화 시킬 수 있는지 궁금해서 공유했습니다.
public class Store {
private Pencil pencil;
public Store() {
this.pencil = new Pencil();
}
}
위와같이 연필을 판매하는 가게 클래스가 있다해보겠습니다. 위 코드에서의 문제점은 Store클래스가 pencil클래스와 강하게 결합되어있는것 입니다.
가령 pencil내부의 코드가 수정되면 아래에 구현된 Store내부의 메소드들도 전부 바뀌어야합니다.
그 문제를 해결하기 위해 필요한게 다형성 즉 인터페이스입니다.
public interface Product {
}
public class Pencil implements Product {
}
연필을 위 코드와 같이 상품이라는 인터페이스를 상속받도록 해줍니다.
그럼
public class Store {
private Product product;
public Store(Product product) {
this.product = product;
}
}
그럼 Store클래스의 코드도 위처럼 변하게되는데 이렇게 되면, 프로그래밍을 하는 입장에서는 product내부의 객체가 연필, 펜, 지우개 등등의 값으로 변경되도 코드의 변경이 거의 없게됩니다.
그리고 위의 객체는 어플리케이션의 실행시점에 등록된 객체로 적절히 받아지고 이러한 이유로 이를 DI(Dependency Injection)이라 부릅니다. 필요한 의존성을 프레임워크에 의해 주입받기 때문이죠.
public List<CommentDto> getComments(Long videoId) {
List<Comment> commentList = commentRepository.findAllByVideoId(videoId);
List<CommentDto> commentDtoList = commentList.stream().map(comment -> {
CommentDto commentDto = new CommentDto(comment);
return commentDto;
})
.collect(Collectors.toList());
return commentDtoList;
}
위의 예제는 Comment 리스트로 stream을 생성한 뒤, 각각의 댓글을 CommentDto 로 변환한 뒤 CommentDto리스트를 반환하는 코드입니다.
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
try{
resolve(f());
}
catch(e){
reject("Error: " + f());
}
}, seconds*1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
const raiseCustomError = (message, statusCode) => {
const err = new Error(message);
err.statusCode = statusCode;
throw err;
};
// 사용 예시
if (!total_price || orderInfo.length === 0) {
raiseCustomError("BAD_REQUEST", 400);
}
customError가 발생했을 때, 에러 메세지를 반환해 주는 함수 입니다. 다양한 경우의 수를 고려해서 에러 핸들링을 해야하는데, 이 때 이를 모듈화해 사용하면 간결한 코드를 완성할 수 있어서 좋았습니다.
구성 요소들이 수평적인 레이어로 조직화되어 있는 다층 구조 입니다. 모든 구성요소가 연결되어 있지만 분리되어있고 총 4개의 계층이 있습니다. 각각의 계층(레이어)는 하위에 있는 레이어에만 의존하는 단방향 의존성 특징을띄고 있습니다.
클래스간 의존성을 클래스 외부에서 주입하는 것을 의미하며 객체가 의존하는 또 다른 객체를 외부에서 선언하고 이를 주입받아 사용하는 것입니다. 이를 사용하는 이유는,
이번 기간을 통해 함수형 프로그래밍을 더욱 잘 활용할 수 있길 기대해 봅니다 : )
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try{
resolve(f());
} catch(err) {
reject(err);
}
}, seconds * 1000)
}
)}
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
과제를 수행하면서 아직 배워가야하는 것들이 많다는 생각이 들었습니다. 해당 기간을 통해 함수형 프로그래밍 과 더 좋은 코드가 무엇인지 고민하며 성장해가고 싶습니다.
본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 code block 을 사용해 올려주세요
함수형으로 짜여진 코드가 없어서 공유를 못합니다.
Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
레이어드 아키텍처 패턴은 정확히 한 패턴에 몇개의 어떤 레이어가 있어야 하는지를 명시하지 않지만, 가장 보편적인 레이어드 아키텍처 패턴은 4개의 레이어로 구성되어있습니다. presentation, business, persistence, database있습니다.
-Presentation layer: UI, 브라우저에서의 유저와의 통신 로직을 다룸.
-Business layer: 요청에 따른 비즈니스 로직을 다룸.
-Persistence layer: DAO(Data Access Object) , ORM과 관련된 데이터베이스에 접근해서 데이터를 저장, 수정하는 함수 로직을 다룸.
-Database layer: 데이터가 다 저장되어있는 곳. 데이터베이스.
핵심 원칙은 한 계층의 모든 요소는 오직 같은 계층에 존재하는 다른 요소나 계층상 "아래"에 위치한 요소에만 의존한다는 것입니다.
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
-개념: 의존성 주입이란 외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴으로, 인터페이스를 사이에 둬서 클래스 레벨에서는 의존관계가 고정되지 않도록 하고 런타임 시에 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있게 해줍니다. 의존성이란 한 객체가 다른 객체를 사용할 때 의존성이 있다고 합니다.
-필요 이유:Test가 용이해지고, 코드의 재활용성을 높여주며, 객체 간의 의존성(종속성)을 줄이거나 없엘 수 있고, 객체 간의 결합도이 낮추면서 유연한 코드를 작성할 수 있습니다.
본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
let arr = [1, 2, 3, 4, 5];
let map = arr.map(function (x) {
return x * 2;
});
console.log(map);
type SomeFunctionReturnString = () => string;
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (err) {
reject(err);
}
}, seconds * 1000);
});
}
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
async getRelatedUsers(_id: string, option: RELATION) {
const user = await this.userRepository.findById(_id);
if (!user) {
throw new BadRequestException('요청한 사용자는 없는 사용자입니다.');
}
const { followings, followers } = user;
const userIdList = option == RELATION.FOLLOWERS ? followers : followings;하기
return await Promise.all(
userIdList.map(async (userId) => {
return getUserBasicInfo(await this.userRepository.findById(userId));
}),
);
}
이유 : Promise all을 처음 사용해보고 속도향상이 되는걸 확인했을 때 뿌듯했던 것 같습니다.
Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
소프트웨어 개발에서 가장 일반적으로 널리 사용되는 아키텍처로, 각 계층은 어플리케이션 내에서의 특정 역할과 관심사(화면 표시, 비즈니스 로직 수행, DB 작업 등)별로 구분된다.
이는 Layered Architecture 의 강력한 기능인 '관심사의 분리 (Separation of Concern)' 를 의미한다.
관심사의 분리가 충족되면 각 계층의 역할을 수행할 수 있는데 집중하여 도메인 로직을 개발할 수 있고 테스트 로직을 작성할 수 있기 떄문에 유지보수에도 용이하다.
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
// 함수를 인자로 전달받고 함수를 반환하는 고차 함수
function makeCounter(predicate) {
// 자유 변수. num의 상태는 유지되어야 한다.
let num = 0;
// 클로저. num의 상태를 유지한다.
return function () {
// predicate는 자유 변수 num의 상태를 변화시킨다.
num = predicate(num);
return num;
};
}
// 보조 함수
function increase(n) {
return ++n;
}
// makeCounter는 함수를 인수로 전달받는다. 그리고 클로저를 반환한다.
const increaser = makeCounter(increase);
console.log(increaser()); // 1
console.log(increaser()); // 2
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise<string>((resolve, reject) => {
setTimeout( () => {
try{
const msg = f();
return resolve(msg);
} catch (e) {
reject(e);
}
} , seconds);
})
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve,reject) => {
setTimeout(()=> {
try{
resolve(f());
}catch(error){
let message = 'unknown error';
if (error instanceof Error) message = `${error.name}: ${error.message}`;
if (error instanceof Object) message = error.toString();
reject(message);
}
}, seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
export class BaseService<T> {
constructor() {}
resObj(data: T): IResObj<T> {
return new IResObj(200, false, 'success', data);
}
resBoolean(data: boolean): IResObj<boolean> {
return new IResObj(200, false, 'success', data);
}
resNumber(data: number): IResObj<number> {
return new IResObj(200, false, 'success', data);
}
resList(data: any): IResObjList<T> {
return new IResObjList(200, false, 'success', data);
}
resError(message: string): IError {
return new IError(HttpStatus.BAD_REQUEST, message);
}
objValidate(obj: T): void {
if (!obj) throw new Error('findedObj not found');
}
}
-> 모든 서비스 레이어에서 사용될 수 있는 기본적인 응답 형태의 함수들을 모아놓은 클래스입니다.
->
리퀘스트에 대한 역할을 분리하여 각각의 레이어는 맡은 바 일에 대한 책임을 다 하는 것입니다.
일반적으로 presentation, business, persistence, data 레이어로 구성되어 있습니다.
presentation: http 통신에 관한 요청에 대한 응답을 담당합니다.
business: 비즈니스 로직이 주로 위치합니다.
persistence: 데이터베이스 접근을 담당합니다.
data: 데이터베이스
->
개념: 추상화된 객체를 외부에서 주입합니다.
필요한 이유 : 여러 클래스에 의존하는 A클래스에서 의존하는 클래스를 생성한 후 문제를 해결하는 경우 비슷한 코드의 중복과 코드가 길어지는 경우가 많아 불핋요한 비용이 발생합니다.
A클래스에서 필요한 객체를 외부에서 주입하도록하여 A클래스의 코드에서 발생하는 비용을 줄일 수 있습니다.
-> 이번 강의를 통해서 배워보겠습니다.
type SomeFunctionReturnString = () => string;
function delay(
f: SomeFunctionReturnString,
seconds: number,
): Promise<string> {
return new Promise((res, rej) => {
setTimeout(() => {
try {
res(f());
} catch (err) {
rej(err);
}
}, seconds * 1000);
});
// 해당 함수 내부를 구현해 주세요
}
const success = () => {
return 'successfully done';
};
const fail = () => {
throw new Error('failed');
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
export type RenameProperties<
T extends {},
M extends { [key: string]: keyof T }
> = {
[K in keyof M]: T[M[K]];
} & {
[K in Exclude<keyof T, M[keyof M]>]: T[K];
};
Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
Persistance, Service, Presentation 레이어 등 여러 레이어로 나누어지는 아키텍쳐입니다.
예를 들어, Persistance에서는 DB에 대한 쿼리 로직, Service에서는 요청을 처리하는 로직, Presentation에서는 데이터를 보여주는 로직 등으로 분리하면, 각 레이어에서 자신이 맡은 일을 정확하게 하고 다음 레이어로 데이터를 전달하면 다음 레이어에서 자신의 역할을 다하고 또 다음 레이어로 전달하는 형태로 구조가 되어 있습니다.
이것은 관심사의 분리를 통해 역할을 적절하게 나누어 각 레이어의 작업을 명확하게 할 수 있습니다.
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
어떤 클래스 A가 B에 대해 의존성을 가지고 있을 때, B의 기능이 변경되면, A 또한 변경됩니다.
이러한 A와 B의 의존성을 분리하기 위해서 사용하는 것이 DI입니다.
DI는 A가 가지는 의존성을 B 뿐만 아니라, C, D 등 다른 객체에도 적용할 수 있도록 해줍니다.
이것은 A의 객체를 생성할 때 다른 객체를 넣어주거나, 저장된 객체를 변경해주는 메서드를 통해서 구현되고는 합니다.
이러한 행위는 의존성을 외부에서 정해주는 것으로 보여지고 의존성 주입이라는 용어로서 나타나게 되었습니다.
DI는 의존성을 느슨하게 해주고, 덕분에 재사용성이 높은 코드 작성을 가능하게 해 줍니다.
하나의 예로 콘서트의 정보를 나타낼 때, List 클래스가 존재하고, 여기에 Timeline을 주입하면, Timeline List가 되고, Artist를 주입하면, Artist List가 되기도 합니다.
이처럼 DI를 통해 재사용성이 좋은 코드를 작성할 수 있습니다.
본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
Array.prototype.map, Array.prototype.forEach 등 JS(TS)에서 기본적으로 지원하는 메서드가 많이 존재합니다.
이러한 함수들은 아래와 같이 사용이 가능합니다.
function adder(add: number) {
return (num: number) => num + add
}
const add10 = adder(10);
console.log(add10(20));
console.log([1, 2, 3].map(add10));
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (error) {
reject(error);
}
}, seconds);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 코드블락을 사용해 올려주세요
<script>
const urlId = document.location.href.split('#')[1]
switch (urlId) {
case 'link1':
location.href="~"
break;
case 'link2':
location.href="~"
break;
}
</script>
js를 사용한 링크 랩핑 코드, 나에게 당장 필요한 것을 잘 만들어 보는 것이 중요한 것 같다.
Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
(코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(function () {
try {
const res = f();
resolve(res);
} catch (error) {
reject(error);
}
}, seconds * 1000);
});
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
async getDogsDistance({ id }) {
const result = [];
const distance = await this.cacheManager.get(id);
for (let i = 0; i < Object.keys(distance).length; i++) {
const tmp = new AroundDogOutput(); //객체 타입 리턴 받기 위해 새로운 타입 지정
(tmp.dogId = Object.keys(distance)[i]),
(tmp.distance = Object.values(distance)[i]);
result.push(tmp);
}
return result;
}
사이드프로젝트로 진행했던 작업 중 일부입니다. redis에 저장되어 있는 객체값을 호출해서 새로운 output 타입에 담아 return 하는 함수입니다.
책임과 성격이 다른 것을 크게 그룹으로 만들어 분리해두는 것
유사한 관심사들을 layer로 나눠서 수직적으로 배열한 것
보통 웹 기반의 엔터프라이즈 애플리케이션은 3개의 계층을 갖는다고 해서 3계층 애플리케이션이라고도 합니다.
Presentation Layer : 웹 기반의 UI를 만들어내고 그 흐름을 관리하는 계층
Service Layer : 비즈니스 로직을 담고 있는 계층
DataAccess Layer : 백엔드의 DB나 레거시 시스템과 연동하는 인터페이스 역할을 하는 계층
(Infrastructure 계층)
이렇게 계층을 나누어 분리하는 이유는 유지 보수할 때 다른 계층에 있는 부분을 수정하지 않아도 되기에 편리하기 때문입니다.
DI(Dependency Injection) 의존성 주입은 Tight Coupling(강한 결합)을 Loose Coupling(느슨한 결합)으로 전환 시키는 방법이며, 제어의 역전(Inversion of Control)의 기술중 하나입니다.
DI(Dependency Injection) 의존성 주입에 총 3가지의 방법이 존재하며 이 중 Constructor Inject(생성자 주입)이 많은 Design pattern에서 권장됩니다.
의존성주입을 사용하는 이유는 코드의 재사용성을 높여주고 테스트에 용이하며 종속성이 감소하기 때문에 변경 및 수정사항에 민감하지 않는다는 장점이 있습니다. 또한 결합도는 낮추면서 유연성과 확장성을 향상 시킬 수 있으며 객체간의 의존관계를 직접 설정할 수 있습니다.
객체를 중심으로 사고하고 프로그램을 작성하는 객체지향 프로그래밍과 달리 함수형 프로그래밍은 데이터를 함수로 연결하는 것을 중심으로 사고하고 프로그래밍을 하는 것을 뜻합니다. 함수형 프로그래밍은 선언형 프로그래밍과 순수 함수를 이용해 문제를 해결하는 프로그래밍 패러다임입니다. 선언형 프로그래밍과 순수 함수 덕에 코드가 간결해지고, 유지보수와 가독성이 크게 증가한다는 장점을 가지고 있습니다.
순수 함수를 활용한 함수형 프로그그래밍 예제
const ret = [1,3,6,8,12]
.reduce((max,num) => num > max ? num : max, 0)
console.log(ret) //12
순수 함수란 출력이 입력에만 의존하는 것을 의미하기 때문에 위의 예제와 같이 순수 함수들을 블록처럼 쌓아 로직을 구현하고 고차 함수를 통해 재사용성을 높인 프로그래밍을 함수형 프로그래밍 언어라고 합니다.
type SomeFunctionReturnString = () => string;
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (e) {
reject(e);
}
}, seconds * 1000);
});
}
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
언어 상관없음
어떤 로직이든 상관없음
단, 길이가 길지 않은 함수 단위가 좋습니다
백준 2133번 타일채우기 답 / 친구가 못풀었는데 나는 풀어서 보면 기분이 좋아지는 코드 입니다.
def n_cases(n):
if n % 2 != 0 :
return 0
else :
k = n//2
answer = [3]
for i in range(1,k):
answer.append( answer[i-1] * 3 + sum(answer[0:i-1]) * 2 +2 )
return answer[-1]%1000000007
하나의 개체를 변경하면 연결된 개체들에 연쇄적으로 영향을 줍니다.
소프트웨어 구조를 주기적으로 변경할 필요가 있는데 아키텍처가 잘 짜여 있으면 소프트웨어 변경을 쉽게 할 수 있어 유지보수하기 수월해집니다. 또한 요구사항에 빠르게 대응할 수 있고 테스트도 빠르게 할 수 있게 됩니다. 아키텍처를 관리하지 않으면 시간이 지날수록 기능을 추가하기 힘들어집니다. 기존 소스 코드를 바꾸는 데 시간이 많이 들기 때문입니다.
계층형 아키텍처(Layered Architecture)는 다음과 같은 계층 구조를 갖습니다.
User Interface
User Interface management
Authentication and authorization
Core business logic/application functionality
System utilities
System support(OS, database etc)
각각의 레이어는 특정 서비스를 제공하도록 구성되어 있으며 같은 목적의 코드들을 모아두어 코드들의 관심사를 분리합니다.
각각의 레이어 사이에는 인터페이스가 있으며 바로 아래 레이어는 바로 위 레이어에 서비스를 제공합니다.
다음과 같은 장단점이 있습니다.
장점 : 인터페이스를 동일하게 유지하면 하나의 계층을 새로운 구현으로 대체할 수 있습니다.
단점 : 계층적으로 명백하게 구분하는 작업이 보통 어렵습니다.
business logic과 인프라스트럭처 사이에 무수한 의존성이 존재합니다.
인프라스트럭처의 변경에 business logic이 영향을 받습니다.
business logic을 이러한 변경으로부터 분리해 보호해야 합니다.
이때 의존성 주입(Dependency Injection 이하 DI)을 통해 이를 달성할 수 있습니다.
의존성 주입이란 클래스에 대한 의존성의 인터페이스 화를 통한 코드 유연성 증대 + 클래스의 인스턴스를 외부에서 생성하여 주입하는 것을 뜻합니다.
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
백엔드가 뭔지 감을 잡고 싶습니다.
1. 본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 code block 을 사용해 올려주세요
- 언어 상관없음
- 어떤 로직이든 상관없음
- 단, 길이가 길지 않은 함수 단위가 좋습니다
updateTree: async (ctx) => { const _ = ctx.request.body if(_.departments) { let index = 0 for(let d in _.departments) { let depart = await models.department.findByPk(_.departments[d].id) depart.order = index depart.depth = 1 depart.save() index++ if(_.departments[d].children) { checkTreeNode(_.departments[d].children, depart.id, 2) } } } else { response.noContent(ctx) } } async function checkTreeNode(nodes, parentId, lv) { if(nodes.length > 0) { nodes.map((node, index)=>{ let depart = await models.department.findByPk(node.id) depart.order = index + 1 depart.depth = lv depart.parentId = parseInt(parentId) depart.save() index++ if(node.children) checkTreeNode(node.children, node.id, lv+1) }) } }
2. Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
3. Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
4. 본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
someArr.map((item,index)=> item.value) someArr.filter((item,index)=> item.value > 10) someArr.sort((a,b)=>a - b) someArr.reduce((cur,nv)=> cur+nv, 0)
5. (코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string function delay(f: SomeFunctionReturnString, seconds: number): Promise<string>{ // 해당 함수 내부를 구현해 주세요 return new Promise((resolve,reject)=>{ setTimeout(()=>{ try { resolve(f()); } catch (e) { reject(e) } },seconds * 1000) }) }; const success = () => { return "successfully done"; }; const fail = () => { throw new Error("failed"); }; delay(success, 2) // 2초 뒤에 successfully done 로그 .then((res) => console.log(res)) .catch((e) => console.log(e)); delay(fail, 2) // 2초 뒤에 failed 로그 .then((res) => console.log(res)) .catch((e) => console.log(e));결과값
$ ts-node delay.ts successfully done Error: failed
6. 강의를 통해서 기대하는 바, 또는 얻고 싶은 팁을 적어주세요
본인이 작성했던 코드 중 공유하고 싶은 코드를 이유와 함께 마크다운 코드블락을 사용해 올려주세요
const sendNotification = async (uId, payload) => {
try {
const subs = await db.LoadSubscribe(uId).then((result) => (result?.subscription ? JSON.parse(result?.subscription) : []));
await Promise.all(
subs.map((sub) =>
webPush.sendNotification(sub, JSON.stringify(payload)).catch(async (e) => {
// 해당 구독 삭제
const prevSubs = await db.LoadSubscribe(uId).then((result) => (result?.subscription ? JSON.parse(result?.subscription) : []));
const subs = prevSubs.filter((prevSub) => !_.isEqual(prevSub, sub));
db.updateSubscribe(uId, subs);
}),
),
);
} catch (e) {
console.error(e);
}
}
해당 코드는 Notifications API 을 활용하여 클라이언트 단에 알림을 보내는 함수입니다.
db에 저장된 endPoint를 가져와 비동기 작업을 Promise.all을 활용하여 병렬 처리하여 속도를 높였으며 작업 중 에러가 발생하는(클라이언트 접근이 중단된) endPoint를 삭제하는 작업도 하고있습니다.
Layered Architecture(계층 아키텍처)에 대해서 설명해 주세요
소프트웨어 개발에 사용 되는 아키텍처로 각 계층은 역할 별로 구분됩니다.
시스템의 결합도는 낮아지고 응집도가 높아집니다.
코드의 재사용성을 높이고 유지보수성을 확장할 수 있습니다.
코드를 분리 하면서 함수별 크기가 줄어들어 모든 도메인을 다 알필요가 없고 테스트 하기에도 용이합니다.
Dependency Injection(의존성 주입)의 개념과 함께, 왜 필요한지 작성해 주세요
의존성 주입(DI)은 하나의 객체가 다른 객체의 의존성을 제공하는 기술입니다.
코드의 결합도를 낮추고 코드의 가독성과 재사용성을 높여주며 테스트하기 용이해집니다.
본인이 사용하는 언어의 Functional Programming(함수형 프로그래밍) 스펙을 예제와 함께 소개해 주세요
const arr = ['1', '2', '3', '4', '5'];
console.log(arr); // [ '1', '2', '3', '4', '5' ]
console.log(arr.filter(v => +v > 3)); // [ '4', '5' ]
console.log(arr.map(n => +n)); // [ 1, 2, 3, 4, 5 ]
console.log(arr.map(n => +n).reduce((v, sum) => sum + v, 0)); // 15
(코드 작성) 다음 스펙을 만족하는 delay 함수를 작성해 주세요 (hint: Promise 사용)
type SomeFunctionReturnString = () => string;
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(f());
} catch (err) {
reject(err);
}
}, seconds * 1000);
});
}
const success = () => {
return 'successfully done';
};
const fail = () => {
throw new Error('failed');
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
after 2 seconds
successfully done
Error: failed
강의를 통해서 기대하는 바, 또는 얻고 싶은 팁을 적어주세요
함수형 프로그래밍에 더욱 적응하며 실무에도 잘 활용하고 싶습니다.
그리고 멘토님의 강의를 통해 실무에서 활용할만한 스킬을 배워보도록 하겠습니다.
해당 code block 에 올려주세요
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
const TodoAddPage = () => {
const [todoInfo, setTodoInfo] = useState({ title: '', content: '' });
const navigate = useNavigate();
const onChange = (e: ChangeEvent<HTMLInputElement>): void => {
setTodoInfo({ ...todoInfo, [e.target.name]: e.target.value });
};
const addTodo = (e: MouseEvent<HTMLElement>) => {
e.preventDefault();
console.log(todoInfo);
createTodo(todoInfo).then(() => {
console.log('add todo successed!');
navigate(`/todo`);
});
};
};
typeScript로 구현한 todo-list의 일부분
처음으로 jsx를 모두 tsx로 변환하여 리팩토링한 프로젝트라서 의미가 있습니다
처음에는 조금 번거롭지만 숨겨진 error를 더 빨리 찾을 수 있다는 장점이 있다고 생각합니다
소프트웨어 개발에서 일반적으로 사용되는 아키텍처 중 하나
각 계층은 어플리케이션 내에서 특정 역할과 관심사(화면 표시, 비즈니스 로직 수행, DB작업 등)별로 구분
사용자가 특정 고객 정보를 요청한 상황을 가정하여, Layered Architecture 가 이 요청을 수행하는 시나리오
사용자가 보고있는 화면(Customer Screen, 흔히 말하는 View 라고 할 수 있을 것 같다)에서 사용자는 고객 정보를 요청한다.
1) 이 요청은 그 요청을 처리할 수 있는 모듈이 무엇인지 알고있는 Customer Delegate (흔히 말하는 Controller 라고 할 수 있을 것 같다) 로 전달된다.
2) Customer Delegate 는 해당 요청을 처리하기 위해 Business Layer 의 Customer Object 로 요청을 다시 전달한다.
3) Customer Object는 요청을 받고 비즈니스 로직을 수행하기 위한 데이터를 얻기 위해, Persistence Layer의 Customer dao 와 Order dao 에 요청을 보낸다.
4) Persistence Layer 의 DAO들은 요청을 수행하기 위해 Database Layer 에 접근하여 데이터를 가져온다.
5) 이 요청은 다시 반대로 Persistence Layer → Business Layer → Presentation Layer 로 전달되고 최종적으로 사용자에게 전달된다.
의존성 주입을 사용하는 이유?
1. 의존성이 줄어든다.
의존한다는 것은 그 의존 대상의 변화에 취약하다는 것이다. (대상이 변화했을 때, 이에 맞게 수정해야함)
의존성 주입으로 구현했을 때, 주입 받는 대상이 변하더라도 그 구현 자체를 수정할 일이 없거나 줄어듬
2. 재사용성이 높은 코드가 된다.
하나의 클래스 내에서 사용하던 클래스를 별도로 구분하여 구현하면 다른 클래스에서 재사용이 가능
3. 테스트하기 좋은 코드가 된다.
두 클래스의 테스트를 분리하여 진행할 수 있음
4. 가독성이 높아진다.
기능들을 별도로 분리하게 되어 자연스럽게 가독성이 높아진다.
https://jenkov.com/tutorials/dependency-injection/dependency-injection-benefits.html
const users = [
{ user: 'joey', age: 32 },
{ user: 'ross', age: 41 },
{ user: 'chandler', age: 39 },
];
// Native - 더 빠름
users.find(function (o) {
return o.age < 40;
});
// lodash
_.find(users, function (o) {
return o.age < 40;
});
const numbers = [10, 40, 230, 15, 18, 51, 1221];
// lodash - 더 빠름
_.filter(numbers, (num) => num % 3 === 0);
// Native
numbers.filter((num) => num % 3 === 0);
type SomeFunctionReturnString = () => string;
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
// 해당 함수 내부를 구현해 주세요
}
// 코드
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
let timeoutId: NodeJS.Timeout;
timeoutId = setTimeout(() => {}, 1000 * seconds);
global.clearTimeout(timeoutId);
return new Promise((resolve, rejects) => {
f();
console.log(`after ${seconds} seconds`);
resolve('successfully done');
rejects('Error: failed');
});
}
const success = () => {
return 'successfully done';
};
const fail = () => {
throw new Error('failed');
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
after 2 seconds
successfully done
Error: failed
기능뿐만 아니라 가독성 좋은 코드를 디자인하고 작성하는 방법을 학습하고 싶습니다
집단 지성의 힘을 얻어 다양한 관점을 배우고 싶습니다
멘토님의 실무 경험에서 나오는 노하우를 전수받고 싶습니다
해당 강의 주제 뿐만 아니라 백엔드 개발의 트렌드를 배우고 따라가고 싶습니다
private static int[] solution(int[] array, int[][] commands) {
int[] answer = new int[commands.length];
int[] real = array;
int i=0;
for(int[] arr : commands){
array = Arrays.copyOfRange(array, arr[0]-1, arr[1]);
Arrays.sort(array);
answer[i] = array[arr[2]-1];
i++;
array = real;
}
return answer;
}
Optional<MaintenanceSub> maintenancesub = GetSubCategory(name);
if(maintenancesub.isEmpty()){
return this.SubCategorys.stream().filter(f -> f.getList().contains(name)).count() > 0;
}else{
return SubCategorys.contains(maintenancesub.get());
}
type SomeFunctionReturnString = () => string
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise<string>((resolve, reject) => {
setTimeout( () => {
try{
const msg = f();
return resolve(msg);
} catch (e) {
reject(e);
}
} , seconds);
})
};
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2) // 2초 뒤에 successfully done 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2) // 2초 뒤에 failed 로그
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
successfully done
Error: failed
let count = [0, 0, 0, 0];
function totalcount(data) {
data.map((e) => {
if (e.choice === 1) {
++count[0];
} else if (e.choice === 2) {
++count[1];
} else if (e.choice === 3) {
++count[2];
} else if (e.choice === 4) {
++count[3];
}
});
let totalcount = count[0] + count[1] + count[2] + count[3];
return totalcount;
}
const VoteRepository = require("../repositories/vote.repository");
class VoteService {
voteRepository = new VoteRepository();
postVote = async (userKey, selectKey, choice) => {
const isSelect = await this.voteRepository.findOneSelect(selectKey);
if (!isSelect) throw new ErrorCustom(400, "해당 선택글이 존재하지 않습니다.");
if (userKey === isSelect.userKey) throw new ErrorCustom(400, "본인 글에는 투표할 수 없습니다.");
if (isSelect.completion === true) throw new ErrorCustom(400, "투표가 마감되었습니다.");
const voteCheck = await this.voteRepository.findOneVote(selectKey, userKey);
if (!voteCheck) {
await this.voteRepository.createVote(selectKey, userKey, choice);
let votePoint = await this.voteRepository.incrementPoint(userKey);
const allVotes = await this.voteRepository.findAllVote(selectKey);
const total = totalcount(allVotes);
function rate(i) {
const num = (count[i] / total) * 100;
return Math.round(num * 100) / 100;
}
return {
1: rate(0), 2: rate(1), 3: rate(2), 4: rate(3),
total,
isVote: choice,
votePoint: votePoint.point,
};
} else {
throw new ErrorCustom(400, "이미 투표를 실시했습니다.");
}
};
}
팀프로젝트를 진행 하면서 게시물에 투표를 실시하는 서비스 로직입니다.
다른 서비스 로직에서도 사용할 수 있게 총 투표수를 합산하는 함수를 만들어 사용했습니다.
DB에서 게시물의 유무, 작성자의 게시물인지, 투표를 진행 했는지 등을 확인하여 게시물에 투표를 할 수 있도록 만들었습니다.
계층을 분리해서 관리하는 아키텍처 패턴
현재 가장 흔하게 사용되고 있는 아키텍처 패턴 중 하나
단순하고 대중적이면서 비용도 적게 들어 모든 어플리케이션의 사실상 표준 아키텍처
계층을 분리해서 유지하고, 각 계층이 자신의 바로 아래 계층에만 의존하게 만드는 것이 목표
클래스간 의존성을 클래스 외부에서 주입하는 것을 뜻함
객체가 의존하는 또 다른 객체를 외부에서 선언하고 이를 주입받아 사용하는 것
let arr = [1, 2, 3, 4, 5, 6];
// map 함수
let double = arr.map((x) => x * 2);
console.log(double); // [2, 4, 6, 8, 10, 12]
// filter 함수
let even = arr.filter((x) => x % 2 === 0);
console.log(even); // [2, 4, 6]
// reduce 함수
let add = arr.reduce((acc, cur) => acc + cur, 0);
console.log(add); // 21
type SomeFunctionReturnString = () => string;
function delay(f: SomeFunctionReturnString, seconds: number): Promise<string> {
return new Promise((res, rej) => {
setTimeout(() => {
try {
console.log(`after ${seconds} seconds`);
res(f());
} catch (err) {
rej(err);
}
}, seconds * 1000);
});
}
const success = () => {
return "successfully done";
};
const fail = () => {
throw new Error("failed");
};
delay(success, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
delay(fail, 2)
.then((res) => console.log(res))
.catch((e) => console.log(e));
결과값
$ ts-node delay.ts
after 2 seconds
successfully done
Error: failed
개발 공부를 시작한지 얼마 되지 않아 기초가 많이 부족한것을 알고 있기에,
다른 사람들은 어떤 방법으로 코드를 짜야는지 어떤 것이 더 옳고, 효율적인지 등
다양한 코드를 보면서 기본적인 지식을 늘리고 더 나은 코드를 만들고 싶습니다.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.