Why is target stored in the block at all? Currently, target is stored in each block. Why is that? Why it is not simply computed by each client based on the previous timestamps from previous blocks? Why it has to be stored within the block?

In other words, doesn't allowing miner to choose the target remove that it is exponentially harder to create a new block? Other miners might have to mine 256 consequent blocks, but I have only to be lucky once and get one block with 8 more zeroes to replace those 256 blocks. While it is less probable, it grows linearly (256x less probable) and not exponentially.
You're presenting it as a choice between either allowing miners to choose the difficulty, or having it be implied by previous history.

In fact, neither is true. The target is determined by history (for the reasons you bring up), and the target stored inside the block header has to match the value determined by history.

At this point, there is no good reason for this anymore, and a design from scratch could IMO drop the nBits value from the header.

I don't know the original reason, but my guess is that it allowed for stronger contextless validation of block headers. Before version 0.10, Bitcoin Core did not use headers-first synchronization, and thus often did not have all parent headers available when a particular block was being received. Being able to verify that the block has valid PoW (even though the difficulty might be wrong) was a very valuable mechanism for preventing DoS attacks.